一个人写代码——我的 Git 工作流
我不在团队里工作。但我仍然用 Git。不是因为我尊重谁的规则——是因为 Git 是我唯一信任的时光机。
我一个人写代码的时候,Git 的用法跟团队不一样。没有 Code Review,没有 CI 门禁,没有"develop 分支只能从 feature 分支合并"。那些规则是为协调一群人有纪律地工作而设计的,我一个人折腾自己,不需要穿别人的制服。
但我还是需要 Git,因为:
- 写了两小时代码发现方向错了,想全部撤回——Git 可以。
- 改了一个文件但不确定改了啥,想看到每一行谁干的——Git 可以。
- 项目跑了两年,想知道当初为什么要写那段奇怪的逻辑——Git 可以。
一个人用 Git,目的是兜底,不是纪律。所以我用的这套流程,核心就三条原则:
原则一:main 永远是能跑的
main 分支只有一条铁律:任何时候从 main 拉出来的代码,必须能编译、能启动、不会炸。
这不意味着 main 上的代码必须是"完美"的。它只意味着:如果你从 main 拉了一份代码,焦虑感应该是 0。它可能功能不完整,但它不会把你带坑里。
我的做法:
- 每次提交到 main 之前,至少跑一次构建(编译通过)。
- 如果项目有测试,跑一下跟改动相关的测试。全量测试跑不动的时候,至少跑局部。
- 不是所有改动都需要完整审查——如果是修一个变量名拼写、改一段注释、加一个空行,直接 push。
main 不是神圣的。但它不是垃圾桶。
原则二:用 branch,但别当麻烦
一个人的分支策略不需要"feature → develop → staging → main"——那是给人多的团队用的。一个人两个分支就够了:
main —— 能跑的代码
dev —— 正在做的代码
dev 是我每天工作的地方。想改什么直接改,commit 随便写,提交频率随心所欲——反正没人看。等我确认方向对了、代码能跑了,把 dev 合并回 main。
合并方式:永远用 git merge --squash + 重写 commit message。
git checkout main
git merge --squash dev
git commit -m "feat: 完整的改动描述"
为什么用 squash?
因为 dev 上的 commit 记录通常是这样的:
"改了一点点"
"忘了加上传"
"修复改坏了的部分"
"再试试"
"行了我去喝酒了"
这些话不需要出现在 main 的历史里。main 的历史应该是干净、可阅读、可检索的。一次改动一个 commit,commit message 直接描述这个改动做了什么。
如果你觉得 squash 丢细节——你可以选择不 squash,但 commit message 必须在合并前重写。记录你自己看的废话不是 Git 的使命。
原则三:rebase 是好东西
一个人写代码的时候,rebase 是比你想象中更常用的工具。
我几乎每天都在用:
# 从 main 拉最新的代码
git checkout dev
git rebase main
为什么不是 merge?因为 merge 会在 dev 上留下一个"从 main 合并"的 merge commit。这个 commit 对你的开发没有意义——它只是告诉你"我在某个时间点和 main 同步过"。而 main 的历史不需要知道你做过这件事。
rebase 的好处:
- 历史是一条直线,没有分叉。
- dev 永远基于最新的 main,减少合并冲突的可能。
- 冲突在 dev 上解决,不在 main 上解决——main 始终干净。
唯一要注意的是:不要 rebase 已经 push 到远程且别人也在用的分支。 但我一个人写代码,不存在这个问题。rebase 随便用。
我的日常流程
打开电脑,开始写代码:
git checkout dev
# ... 写了一堆代码 ...
git add -p # 挑选要提交的改动
git commit -m "fix: 修复了某处边界条件" # 小改
git add -A && git commit -m "wip" # 大改先暂存
# ... 继续写 ...
git add -A && git commit -m "feat: 完整的功能描述"
# 推送到远程(随便推,没人看)
git push origin dev
# 功能完成了,合并到 main
git checkout main
git merge --squash dev
git commit -m "feat: 最终的功能描述"
# 推 main
git push origin main
# 继续 dev 上干活
git checkout dev
git rebase main
全程大概 5-10 秒的 git 操作时间。剩下的时间全是写代码。
commit message 怎么写
我不用"必须遵循 Conventional Commits"那套僵化的东西,但我自己用了一种简化版:
feat: 新功能
fix: 修bug
refactor: 重构
docs: 文档/注释
style: 格式/拼写
chore: 构建/部署/工具
test: 测试
加一个动词很简单:feat: 添加 HTTP 请求重试机制,比 修改了一些代码 有用一万倍——六个月后你翻 log,前者一秒告诉你干了什么,后者你需要点开 diff 看。
就这一条规则。多了记不住,少了不够用。
tag 怎么打
功能上线(或者 self-host 服务升级)之后,打 tag:
git tag -a v1.2.0 -m "描述这次上线的核心变动"
语义版本号。但说实话,一个人写项目不需要严格遵循 semver——你的用户只有你自己。v1.2.0 还是 v2.0.0,你说了算。
关键不是版本号的规范,是tag 的存在本身——当你需要回退到"三个月前那个还能用的版本"时,tag 是你的救命绳。
总结
一个人的 Git 工作流,不需要学大厂的流程。你不需要 Git Flow,不需要 Gerrit,不需要 CI/CD 门禁。
你只需要三条原则:
- main 不能炸。
- 历史要干净可读。
- rebase 是你的朋友。
工具是为你服务的,不是反过来。
Coding Alone — My Git Workflow
I don't work in a team. But I still use Git. Not because I follow anyone's rules — because Git is the only time machine I trust.
When you code alone, Git usage changes. No code reviews. No CI gates. No "feature branches must merge to develop." Those are coordination tools for groups. I don't need someone else's uniform to dress myself.
But I still need Git because:
- Two hours of code going the wrong way — Git undoes it.
- Wondering what changed in a file — Git shows you.
- A year later, asking "why did I write that weird logic" — Git tells you.
For a solo dev, Git is your safety net, not your discipline system. My workflow runs on three principles.
Principle 1: main Must Run
One iron rule for main: at any time, code pulled from main must build, start, and not crash.
"Not crash" doesn't mean perfect. It means zero anxiety when you check out main. Incomplete features are fine. Broken code is not.
My practice:
- Build before every push to main.
- Run relevant tests if they exist.
- Typos, comments, blank lines — push without ceremony.
Main isn't sacred. But it's not a dumpster.
Principle 2: Branch, But Don't Overthink It
For one person, two branches is enough:
main — code that runs
dev — code in progress
dev is my daily workspace. Commit frequency? Whatever. Commit message quality? Whatever. Nobody reads them. Squash it all when merging back.
git checkout main
git merge --squash dev
git commit -m "feat: complete feature description"
Squash turns this:
"small fix"
"forgot to upload"
"fixed what I broke"
"trying again"
"gonna go drink now"
Into one clean commit. Main's history should be readable and searchable. One change, one commit, one message that describes what it actually does.
If squash loses too much detail — don't squash, but rewrite the message. The junk commits you wrote for yourself don't belong in main.
Principle 3: rebase Is Your Friend
Rebasing daily:
git checkout dev
git rebase main
Why not merge? Merge creates a "sync with main" commit that adds nothing to your understanding. Dev should always be based on the latest main.
Benefits of rebase:
- Linear history, no branch forks.
- Dev is always up to date — fewer merge conflicts.
- Conflicts resolved on dev, not main. Main stays clean.
Only warning: don't rebase branches that others have pulled from. For solo work — this doesn't apply. Rebase freely.
My Daily Flow
git checkout dev
# ... write code ...
git add -p && git commit -m "fix: boundary condition"
git add -A && git commit -m "wip"
# ... more code ...
git add -A && git commit -m "feat: complete feature"
git push origin dev
# Feature done, merge to main
git checkout main
git merge --squash dev
git commit -m "feat: final description"
git push origin main
# Back to dev
git checkout dev
git rebase main
Git takes 5-10 seconds total. Everything else is writing code.
Commit Messages
Simplified Conventional Commits:
feat: new feature
fix: bug fix
refactor: restructuring
docs: documentation / comments
style: formatting / typos
chore: build / deploy / tooling
test: tests
"feat: add HTTP retry mechanism" beats "modified some code" by a factor of 10,000 six months later. That's the whole rule.
Tags
After deploy (or self-host upgrade):
git tag -a v1.2.0 -m "describe the key change"
Semantic versioning, loosely. Your only user is yourself — you set the rules. The point isn't the version number — it's that the tag exists for when you need to go back three months.
Bottom Line
Solo Git doesn't need Git Flow, Gerrit, or CI gates. Just three rules:
- main doesn't break.
- History is clean and readable.
- rebase is your friend.
The tool works for you. Not the other way around.