1. 我给个人项目重新定 Git 流程的起因
1.1 一次误删分支让我开始反思
在讲具体流程之前,我想先说一件真实的小事故。
去年年中,我在家里电脑上整理一个写了快两年的个人博客项目。当时觉得某个功能分支已经没用了,准备清理掉,结果手一抖敲错了分支名,把一个还没合回主线、存着三天改动成果的分支直接删了。我当时的第一个反应是“完蛋了”,第二个反应是去翻 .git 目录下的 reflog 碰碰运气。好在那三天里我每天至少提交一次,最终用 git reflog 把丢掉的引用找回来,分支内容毫发无损。
这件事给我的触动很大。我原本以为一个人写项目,不需要什么流程,反正代码都在本地,能跑就行。但那次之后我意识到:个人开发不代表没有风险,也不代表不需要恢复手段。真正缺的只是适合个人的轻量流程。
从那以后我开始系统性地整理自己用 Git 的方式,从分支命名、提交规范、合并策略到远程推送节奏,逐步形成了一套不太折腾但很实用的流程。这篇文章就是想把这些实践完整地分享出来。
1.2 个人流程和团队流程的本质差异
我在公司里参与过团队项目的 Git 管理,也帮别人搭过仓库,实际情况是:很多人把团队里那套流程硬搬到个人项目上,结果要么难受无比,要么干脆放弃维护。
团队流程和个人流程的本质差异在哪?我用一句话概括:团队流程的核心是“多人协作的秩序”,个人流程的核心是“单人恢复的便利”。 前者往往需要严格的分支权限、Code Review 约定、合并门禁、规范化的 CI 检查;后者根本不需要这些,个人项目里的每一个提交、每一次合并,最终服务对象只有你自己,所以原则应该是“少操作、快恢复、可回溯”。
举个最简单的例子。团队项目里 master 分支往往受保护,不允许直接 push,个人项目不需要,只要你愿意,直接在 master 上提交也没人拦你。但“可以”不等于“应该”。当你把这一条升级成“master 永远保持可以发布的干净状态”,后续的一切都会变得从容。
1.3 这套流程的目标:省事、可追溯、不焦虑
我在设计个人 Git 流程时没有贪多,只定了三个目标。
第一个是省事。我不希望每提交一次代码都要想半天“下一步执行什么命令”,所以流程必须贴近直觉,常用操作最好一条命令能搞定。第二个是可追溯。不管是一个月前改过的一行配置,还是某个功能在哪一天有了第一次可用版本,都能通过 Git 历史快速查清楚。第三个是不焦虑。哪怕误删分支、误重置文件、误提交了大量不该提交的内容,我知道怎么恢复,不会慌。
后续所有内容都是围绕这三个目标展开的。如果看完你也能在你自己的项目里建立一个差不多的节奏,并且觉得“原来个人开发也可以这样清晰”,那这篇文章就没白写。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础准备:把 Git 环境一次性调顺手
2.1 安装和全局配置里最该认真对待的几项
如果你用的是 Windows,最简单的方式是去 Git 官网下载安装包,安装时一路默认即可。macOS 上通常自带了 Git,但版本可能偏旧,建议通过 Homebrew 安装最新版:brew install git。Linux 发行版里一般直接 sudo apt install git 或 sudo yum install git。
很多人装完 Git 就直接开始用,忽略了几个值得认真对待的全局配置。
第一个是 user.name 和 user.email。提交信息里会一直挂着这两个字段,将来查历史的时候如果看到“unknown”或者一串乱码邮箱,基本就是当时没配置干净导致的。我的建议是设置成自己常用的英文名和真实邮箱,并且保持所有设备一致,这样换电脑之后历史记录依然完整。
bash复制git config --global user.name "YourName"
git config --global user.email "you@example.com"
第二个是默认文本编辑器。有时候执行 git commit 或 git rebase 会弹出编辑器让你写信息,默认的 Vim 会让很多人懵住。我习惯设置为 VS Code:
bash复制git config --global core.editor "code --wait"
第三个是行尾符号处理。这一点很容易被忽略,但跨平台协作或者一个人多设备同步时,问题非常烦人。Windows 上的行尾默认是 CRLF,macOS/Linux 上是 LF,如果配置不当,每次打开文件都会看到大量“红色修改”。个人项目我推荐统一用 LF,提交时保留原样:
bash复制# Windows 上推荐
git config --global core.autocrlf false
git config --global core.eol lf
这样设置之后,不管在哪台设备上克隆仓库,文件行尾都不会被乱改,Git 历史自然干净得多。
2.2 用 SSH Key 打通 GitHub/Gitee 的免密推送
服务器在国内的话,常用 Gitee,国际上用 GitHub,不管哪个平台,都强烈建议用 SSH 方式连接仓库,而不是每次 push 都输用户名密码。
配置步骤很固定:先检查本机是否已有密钥,没有就生成一把。
bash复制ls -al ~/.ssh
ssh-keygen -t ed25519 -C "you@example.com"
一路回车生成完毕后,把公钥添加到代码托管平台即可:
bash复制cat ~/.ssh/id_ed25519.pub
在 GitHub 的 Settings → SSH and GPG keys 里新增,Gitee 则在安全设置里粘贴。之后把远程地址从 HTTPS 改成 SSH 格式,比如:
bash复制git remote set-url origin git@github.com:username/repo.git
验证是否生效:
bash复制ssh -T git@github.com
第一次连接时会提示确认主机指纹,输入 yes 就行。SSH 方式的好处不仅仅是免密,更重要的是无法用密码方式通过 SSH 提交,公共电脑上就算别人站你身后,也没法轻易改你的提交凭据,安全性好很多。
2.3 提前准备好全局忽略文件,避免垃圾进仓库
个人项目里很容易出现各种临时文件:IDE 配置、缓存目录、系统生成的 .DS_Store、日志文件等等。如果每次新建仓库时都写一遍 .gitignore,迟早会漏。更好的方式是先准备一个全局忽略文件。
全局忽略文件的位置可以放在用户目录下,比如 ~/.gitignore_global,内容按你自己的开发习惯填写,我这份大概长这样:
gitignore复制.DS_Store
Thumbs.db
.vscode/*
!.vscode/settings.json
.idea/
*.log
node_modules/
dist/
build/
*.class
.env
然后再告诉 Git 全局使用这份忽略文件:
bash复制git config --global core.excludesfile ~/.gitignore_global
这样新建任何仓库时,这些常见垃圾都会被自动过滤。当然,具体的项目里可能还需要针对业务目录单独写 .gitignore,两者配合使用就行。
那么实际操作中,还有一个很多人不知道的小技巧:在提交前先看一眼状态和差异,再决定要不要改暂存区。
bash复制git status
git diff
我见过不少人习惯闭着眼执行 git add . && git commit,如果这个项目没有忽略文件,很容易把几百兆的构建产物提交进去,等发现时历史已经被污染了。养成“先 status 再 diff 再提交”的习惯,这一步本身就能为你省下无数个清理的周末。
3. 个人版分支模型:主线稳定,辅线轻量
3.1 为什么我不照搬企业里的 Git Flow
Git Flow 是很多团队的首选流程:master 用来发布,develop 用来集成,feature/* 用来开发功能,还有 release/* 和 hotfix/*。这套模型在多人协作、定期发布的项目里确实很好用,但放在个人项目上就有些“杀鸡用牛刀”了。
个人项目通常只有你一个角色在开发,并不存在“开发人员互不干扰”的问题,也不需要并行跑一个 release 分支和下一个版本开发分支。照搬 Git Flow 会导致大量精力花在处理分支间同步上,而真正有价值的时间应该花在写代码上。
我的建议是:把企业流程压缩成一个非常轻量的版本。 全仓库只保留一条长期稳定的主线,其他分支全部是临时性的功能分支或修复分支,做完就合,合完就删。
3.2 master 上只放能跑的东西,开发在 dev 上展开
个人项目的默认分支通常叫 master 或 main。我对它的定位是“绝对干净”的状态:这个分支上的每一个提交,都应该对应一个完整可用、能跑通、说得清楚的状态。
这个要求听起来很简单,做到却不容易。因为平时开发过程中,你一定会经历“改了一半”的状态。如果直接在 master 上开发,半天后可能提交了一个“改了一半还编译不过”的节点。这个节点对你自己可能只是找代码的中转站,但一个月后再看,就会成为污染历史的一部分。
所以我习惯在仓库里保留一条 dev 分支,作为日常工作区。所有零碎提交、实验性尝试,先在 dev 上完成。确认没问题之后,再把相对完整的版本合并到 master。这样 master 的历史就会比较规整,dev 则可以随意一些。
实际操作时,分支创建和切换的命令都非常简单:
bash复制git checkout -b dev
如果仓库已经开发过一段时间,也可以直接从当前状态拉一条 dev 出来,不会影响现有内容。
3.3 feature 分支的创建、提交流程、合并与删除
当我要做一个新功能或者修复一个 bug 时,不会直接在 dev 上写,而是从 dev 切一个临时分支。这样带来的好处是:功能开发过程中不管出什么幺蛾子,都不会污染 dev 的干净状态;一旦功能终止或者方案被推翻,直接删分支就行,不需要处理一堆五花八门的提交。
创建分支的习惯命令:
bash复制git checkout dev
git pull origin dev
git checkout -b feature/xxx
分支命名尽量体现意图。我一般用 feature/ 前缀表示新功能,用 fix/ 前缀表示缺陷修复,后面跟上简短的小写英文描述。比如 feature/post-comment-notify、fix/page-crash-on-empty-list。
开发过程中按自己的频率提交,切忌攒一堆改动再一次性提交。提交信息我放在下一节详细说,这里先只看流程。当功能开发到可验证的状态时,回到 dev 分支,合并这个功能分支:
bash复制git checkout dev
git merge --no-ff feature/xxx -m "Merge feature/xxx into dev"
带 --no-ff 合并会保留一个合并提交节点,也就是完整的“功能分支存在过”的证据。对个人项目而言,这个证据在将来回看设计思路时很有价值,所以我通常都带着它。
合并成功后顺手删掉本地和远程这个分支:
bash复制git branch -d feature/xxx
git push origin --delete feature/xxx
删除本地分支时使用 -d 而不是 -D 有个好处:如果分支还有未合并的提交,Git 会提醒你,避免误删。这也是我吃过大亏之后养成的习惯。
4. 提交规范与历史整理:让 log 自己会说话
4.1 我的提交信息模板和几个高频动词
个人项目提交信息如果写得随意,三个月后再看,跟看天书没区别。我见过最夸张的提交记录是连续十几次都叫“更新”或“fix”,完全没法定位问题。这不是工具的问题,是提交信息没有有效承载变更信息的问题。
我给自己定了一套很简单的模板,基本遵循“类型 : 简述”的格式,类型用几个固定动词:
feat:新功能fix:缺陷修复refactor:重构,不涉及功能变化docs:文档调整style:代码风格调整,不影响逻辑chore:构建流程、依赖等杂项
实际提交时就像这样:
bash复制git commit -m "feat: 增加文章封面图上传功能"
git commit -m "fix: 修复列表页在空数据时崩溃的问题"
如果改动比较复杂度,我会在提交信息里写一段更具体的描述。比如用 git commit 打开编辑器后,第一行写类型和标题,空一行,再写详细说明:
text复制feat: 增加积分排行榜接口
- 新增 /api/rank/list 接口
- 支持时间范围过滤和分页
- 附带基础单元测试
这套规范不追求复杂,核心作用就是让你在一个月甚至半年后打开 git log --oneline,能快速知道这个提交大概做了什么,而不是靠猜。
4.2 用 rebase 整理历史,而不是甩一堆 fixup
个人开发过程中,功能分支上往往会积累很多小提交,其中不少是“补了一个字母”“语法错误”“测试又没过”这类中间状态。这些提交在功能完成前是必要的,但如果原封不动都合回主干,历史会变得很嘈杂。
我喜欢在合并之前做一次交互式 rebase,把这些零散提交清理干净:
bash复制git rebase -i HEAD~5
执行后编辑器会列出最近的 5 条提交,你可以把某几条标注为 s(squash,压缩提交),这样它们就会合并成一条更完整的提交;也可以用 f 直接把某条丢掉。这是我日常整理历史最常用的命令。
使用 rebase 时有个很重要的前提:只对你自己的、还没有推送到远程或者大家都拉取过的分支做操作。 如果已经 push 到共享远程,后面又做了 rebase,那么别人再 pull 时会遇到大量冲突。个人项目的功能分支一般不存在这个问题,但如果你把 dev 推到了远程,那就尽量少对 dev 执行交互式 rebase,否则远程历史跟本地会分叉。
整理完提交之后再合并回 dev,效果比一堆“临时提交”好太多。日志会显示一个干净的功能提交,清晰记录这个功能完成了什么,既不会丢过程,也不会被琐碎干扰。
4.3 管理 tag 与 release 版本,回滚时有路可走
我见过不少个人项目,代码一直在推进,版本号却不维护;等到哪一天用户或自己需要线上回滚到某个老版本时,才发现根本找不到“稳定版”节点。
打 tag 的习惯最好从第一个可用版本就开始培养。每当你觉得 dev 上积累的功能已经达到一个可发布的状态,合并到 master 之后,就顺势打一个版本标签:
bash复制git tag -a v1.0.0 -m "第一个正式版本:基础博客功能"
git push origin v1.0.0
打 -a 注释版标签,会在 tag 里记录打标签的时间、作者和说明,配合 -m 写清楚的说明。以后不管你推进到哪个版本,只要需要回滚到某一时刻,直接基于那个 tag 拉分支或 checkout 就能恢复,完全不需要靠记忆翻提交记录。
版本号我通常遵循语义化版本:主版本号.次版本号.修订号。有破坏性变更时升主版本,有新功能时升次版本,只修 bug 时升修订号。个人项目规模小,不一定要严格执行,但有这个意识会让回滚和定位问题容易很多。
5. 单人远程协作:remote 不只是用来备份
5.1 为什么个人项目也应该push到远程
有些人觉得“我自己电脑上写代码,不需要远程仓库”。这个想法我第一次丢硬盘时就彻底改变了。本地代码一旦出现硬件故障、误操作、系统重装,所有项目可能一夜之间全部消失。把 Git 仓库推送到 GitHub、Gitee 或自建服务器上,本质上就是一份离线的异地备份,而且这份备份天然还有版本历史,比任何网盘同步都好用。
远程仓库还让我能够在不同设备间切换。我在办公室改到一半回家继续写,只需要在办公室执行一次 push,回家后 pull 一下,就能接着上次的状态工作。这个体验远比拿着 U 盘拷文件舒服,也比网盘同步可靠。
我的习惯是:任何一个项目的第一件事就是创建本地仓库,并立刻关联远程仓库,然后做首次 push。 之后每完成一个相对完整的小阶段,就把 dev 或 master 推一次,节奏不用太密,也不用刻意等一个功能全部做完。
5.2 多设备同步的冲突处理实践
多设备同步最容易遇到的问题就是冲突。场景通常是这样:办公室在家里的电脑上提交了某个文件,回家之后继续改同一个文件,两台电脑的 Git 历史就分叉了。此时 git pull 会触发 merge,如果改动的是同一行,就直接冲突。
处理冲突的关键是先弄清楚发生了什么。我的流程是:
bash复制git pull origin dev
git status
如果出现冲突提示,Git 会列出冲突文件。打开文件后你会看到这样的内容:
text复制<<<<<<< HEAD
自己的代码
=======
远程的代码
>>>>>>> 远程分支名
手动把不需要的部分删掉,保留正确内容,然后重新提交。这个过程不复杂,但很容易出心理恐惧,很多初学者一看到 <<<<<<< 就慌。其实只需要记住:箭头和等号是分隔符,不是代码,不要保留它们。
为了避免频繁出现这种冲突,我的经验是:切换设备之前,先把当前设备上的工作提交并 push;换到另一台设备上后,第一件事就是 git pull,再开始写代码。 绝大多数冲突都是因为上一台设备该推的没推,下一台设备在旧代码上硬着头皮写,结果两边各自改了同一行。
5.3 用远程仓库里的一张表查看各分支状态
你可能见过团队项目里复杂的分支状态图,个人项目不需要,但日常清楚“哪些分支是活动的、哪些已经合并了”仍然很有必要。
bash复制git branch -a
git branch --merged dev
git branch --merged dev 能列出所有已经合并进 dev 的分支,这些基本都可以随时安全删除。定期清理远程分支也同样重要:
bash复制git remote show origin
这条命令会显示本地分支、远程分支、过期分支的跟踪关系,以及哪些本地分支已经删除但远程还在。每月跑一次,能保持仓库整洁,也能避免有一天发现自己莫名多出了几十个过期的远程分支。
如果你愿意把自动化再往前推一步,可以在远程仓库上配置简单的 CI/CD。拿 GitHub Actions 为例,在项目根目录放一个 .github/workflows/build.yml,push 后自动跑测试或构建,这样个人项目的代码质量也会有一个客观的“守门员”。这个对个人项目完全够用,不需要再搭一套完整 CI 平台。
6. 日常救命命令清单与踩坑记录
6.1 高频命令清单(按场景分类)
这里我把日常用得最多的 Git 命令整理成一张速查清单。不需要背,但建议把这份表格存到书签或笔记里,用得多自然就记住了。
| 场景 | 命令 | 说明 |
|---|---|---|
| 查看状态 | git status |
看有哪些改动、哪些暂存了 |
| 查看差异 | git diff |
看工作区与暂存区的差异 |
| 暂存并提交 | git add -A && git commit -m "..." |
个人常用一条链式提交 |
| 查看历史 | git log --oneline --graph --decorate |
带树状结构的简洁日志 |
| 撤销工作区修改 | git checkout -- <file> |
把文件恢复到最后一次提交状态 |
| 撤销暂存 | git reset HEAD <file> |
把文件从暂存区移出,不丢修改 |
| 回退到上一个提交 | git reset --soft HEAD~1 |
保留改动,只撤销提交 |
| 彻底回退某个文件 | git restore <file> |
新版 Git 更直观的命令 |
| 找回丢失的分支/提交 | git reflog |
查看所有 HEAD 移动历史 |
| 合并指定分支 | git merge --no-ff <branch> |
保留一个合并节点 |
| 重写提交历史 | git rebase -i HEAD~n |
压缩、编辑、删除历史提交 |
6.2 误删、误提交、历史改写失败后的补救
这一部分我想把最常遇到的“灾后现场”一次性讲清楚,每个场景我都实际踩过。
误删分支怎么办? 只要你还记得大概的时间点或提交哈希,git reflog 基本都能救回来。reflog 记录了 HEAD 每一次移动的位置,包括已经被删除分支的引用。执行 git reflog 后找到那段时间的提交哈希,然后基于它重新创建分支:
bash复制git branch feature/restored abc1234
误提交了不该提交的文件怎么办? 如果只是还没推送到远程,用 git reset --soft HEAD~1 把这次提交撤回,文件会恢复到暂存区,然后再用 git reset HEAD 取消暂存,把文件从暂存区移出,修改内容仍然保留在工作区,可谓无损撤销。
rebase 到一半发现冲突太多怎么办? 不用硬着头皮解决,可以随时中止:
bash复制git rebase --abort
这条命令会回到 rebase 开始前的状态,所有没改完的提交都恢复原样。个人项目里我发现这个命令的使用率比想象中高,所以非常建议你记下来。
push 到远程之后又想改提交信息怎么办? 只要这个分支只有你一个人用,且确信别人没有拉取过,可以执行:
bash复制git commit --amend
git push --force-with-lease
注意用法是 --force-with-lease 而不是 --force。前者会在推送前检查远程是否被其他人改动过,如果有改动则拒绝推送,能避免一个“force push 把别人代码冲掉”的经典事故。个人项目中即使只有你自己,我也推荐用这个安全版本。
6.3 用习惯沉淀下来的几条流程心得
流程最终是死的,真正起作用的是习惯。总结几条我这几年跑下来最有体感的经验。
第一,提交要频繁,合并要克制。 开发过程中大胆提交,记录思路和过程;但合到主干之前,要把零散提交整理成有意义的功能块,再用合并节点体现边界。
第二,日志是你的第二个大脑。 很多“我当时为什么这么写”的问题,其实都能从良好的 git log --oneline 和提交信息里找到答案。养成写着清楚提交信息的习惯,等于给自己以后留了一本开发日记。
第三,reflog 是好东西,但要讲科学。 它默认只保留几十天的记录,所以当你发现误删了分支,越早处理越好。拖得越久,那些被 GC 掉的旧对象就越难找回。
第四,远程仓库不只是网盘。 它承担了备份、协作、CI 触发、版本分发等多个角色。哪怕项目再小,也值得在创建仓库后的几分钟内完成连接和首次推送。
我现在做任何新项目,第一步就是初始化仓库、配置远程、写忽略文件,然后才开始写代码。整个流程不到三分钟,但之后每一分钟都能省下不清的功夫。希望这份个人开发流程,能让你在独自写代码时也心里有底。
