ZJH

让每次修改都有来路:Git 的日常开发指南

从 Git 的基本概念、常用命令、checkout 习惯到团队协作和效率工具,整理一份日常开发可复用的 Git 指南。

Git 经常被解释成“版本控制工具”,这当然没错,但还不够。真正把 Git 用顺以后,它更像一个保存判断、组织协作、降低回滚成本的工作系统。

这篇文章不追求把所有命令都列完,而是回答四个更实际的问题:

  • Git 到底在管理什么。
  • 日常开发里最常用的命令分别适合什么场景。
  • 团队协作时怎样用 Git 少制造麻烦。
  • 哪些插件和工具能明显提高效率。

Git 是什么

Git 是一个分布式版本控制系统。版本控制系统会记录项目历史,让你知道“谁在什么时候,因为哪件事改了什么”,也让你能回到以前的版本。分布式的意思是:每个开发者本地都有一份完整仓库和历史,不是所有动作都必须依赖远端服务器。

理解 Git,先抓住几个概念就够了。

仓库

仓库是 Git 管理的项目。它包含当前文件,也包含这些文件的历史。平时你看到的是工作目录,真正的历史和对象数据保存在隐藏的 .git 目录里。

提交

提交是 Git 历史里最重要的单位。你可以把一次提交理解成项目在某个时间点的一张快照。Git 不是只把它当成一串零散补丁,而是围绕快照、对象和引用来组织历史。

一个好的提交最好能独立说明一次完整意图,比如:

git commit -m "feat: add search shortcut"

工作区、暂存区、本地仓库、远端仓库

日常最常见的流动路径是:

工作区 -> 暂存区 -> 本地仓库 -> 远端仓库

对应到命令就是:

git status
git add src/pages/index.astro
git commit -m "feat: add homepage search"
git push

工作区是你正在编辑的文件;暂存区是下一次提交会包含的内容;本地仓库保存已经提交的历史;远端仓库通常是 GitHub、GitLab、Bitbucket 这类协作平台上的共享仓库。

暂存区很重要。它允许你从一堆本地改动里挑出“这一组先提交”,把不同目的的修改拆开。

分支

分支可以理解成一条独立的开发线。你可以在分支上尝试功能、修复 bug、整理文章,确认没问题后再合回主分支。

git switch main
git pull --ff-only
git switch -c feature/search-shortcut

如果你更习惯 checkout,也完全没问题。很多团队和老项目文档里仍然大量使用它:

git checkout main
git pull --ff-only
git checkout -b feature/search-shortcut

switch 的好处是语义更单一:它只负责切分支;checkout 同时负责切分支和还原文件,所以能力更老练,也更容易在疲惫的时候误用。我的建议不是强行换掉肌肉记忆,而是记住这条边界:切分支可以继续用 checkout,丢弃文件改动前一定先看 git statusgit diff

分支不是为了让历史变复杂,而是为了隔离正在进行的工作。一个任务一条分支,是最容易维护的起点。

常用命令和场景

创建和获取仓库

新项目从本地开始:

git init
git add .
git commit -m "chore: initial commit"

已有远端项目拉到本地:

git clone git@github.com:owner/repo.git

进入一个陌生仓库后,先看三件事:

git status
git branch --show-current
git log --oneline --decorate -n 10

这能告诉你当前在哪个分支、工作区是否干净、最近发生过什么。

查看改动

写代码过程中,statusdiff 是最值得高频使用的两个命令:

git status
git diff
git diff --staged

git diff 看还没暂存的改动,git diff --staged 看已经进入下一次提交的改动。提交前先看 diff,可以减少误提交文件、调试代码和临时内容。

暂存和提交

把所有改动都放进一次提交很方便,但不一定健康。更推荐按意图暂存:

git add src/components/SearchShortcut.astro
git add src/pages/index.astro
git commit -m "feat: add homepage search shortcut"

如果一个文件里混了两类改动,可以交互式选择:

git add -p

这条命令很适合把“功能实现”和“顺手格式化”拆开。提交越清楚,review 和回滚越省力。

分支切换和协作

创建任务分支,可以用更明确的 switch

git switch -c feature/git-guide

也可以用你更常用的 checkout

git checkout -b feature/git-guide

切回已有分支同理:

git switch main
git checkout main

查看本地分支:

git branch

删除已经合并的本地分支:

git branch -d feature/git-guide

分支命名建议包含类型和主题,例如:

feature/search-shortcut
fix/rss-draft-filter
content/git-guide
chore/update-ci

拉取和推送

从远端更新信息:

git fetch origin

更新当前分支:

git pull --ff-only

第一次推送新分支:

git push -u origin feature/git-guide

fetch 只是把远端信息拿回来,不会直接改你的工作分支;pull 会在获取后尝试合并或变基。刚开始不确定时,可以先 fetch,再用 git loggit diff 看清楚差异。

合并、变基和整理历史

把主分支更新合进当前分支,常见有两种方式。

合并:

git merge origin/main

变基:

git rebase origin/main

merge 会保留分支汇合的历史形状;rebase 会把你的提交“搬到”新的基底之后,让历史更线性。日常建议是:自己的功能分支可以 rebase,已经多人共享或已经发布出去的公共分支不要随便改写历史。

整理本地提交时,可以用交互式 rebase:

git rebase -i origin/main

常见动作有:

  • reword:修改提交信息。
  • squash:把多个修补提交合并。
  • drop:丢掉某个不需要的提交。

不要为了“历史漂亮”过度整理。好的历史应该帮助理解,不是把真实工作过程化妆成毫无波澜。

临时保存工作

正在写一半,需要临时切分支处理别的事:

git stash push -m "wip: search layout"
git switch fix/urgent-issue

回来以后恢复:

git stash list
git stash pop

如果这份改动已经比较重要,更推荐直接提交到临时分支。stash 适合短期收纳,不适合长期存档。

撤销和回滚

取消暂存但保留文件改动:

git restore --staged src/pages/search.astro

丢掉某个文件的本地未提交改动:

git restore src/pages/search.astro

如果你还在用 checkout,对应的旧写法是:

git checkout -- src/pages/search.astro

这条命令会丢掉该文件未提交的本地改动。它很好用,也很锋利;如果只是取消暂存,不要用它。

撤销已经提交并公开的改动:

git revert <commit>

回退本地提交但保留改动:

git reset --soft HEAD~1

这里最需要谨慎的是 reset。它可以很方便地重写本地历史,但如果用在已经推送且有人基于它继续工作的分支上,就容易影响别人。公共历史优先用 revert

工作中怎么用好 Git

让分支对应一个任务

一个分支最好只服务一个明确目标:一个功能、一个 bug、一次内容改动、一次维护升级。分支范围越清楚,PR 描述、代码 review 和最终回滚都越简单。

提交按意图拆分

提交不一定只能改一个文件,但它应该只表达一个意图。比如做搜索入口,可以拆成:

feat: add reusable search shortcut
feat: show search shortcut on directory pages
test: cover search entrypoints

比起一个巨大的 feat: update site,这种历史更容易 review,也更容易在出问题时定位。

一个简单判断:如果你很难用一句话说明这个提交做了什么,它可能就太大了。

提交信息写结果

提交信息不是写给 Git 的,是写给未来的人看的。它应该描述代码库获得了什么变化,而不是记录你做了什么动作。

推荐:

feat: add archive page
fix: exclude draft entries from RSS
docs: document content publishing workflow

不推荐:

update files
fix stuff
change code

如果团队使用 Conventional Commits,可以把 featfixdocstestchore 这类前缀固定下来。重点不在格式本身,而在让历史可以被快速扫描。

可以直接参考这份规范:约定式提交 1.0.0。它给出的基本结构是:

<type>[optional scope]: <description>

日常先掌握这几个类型就够了:

  • feat:新增能力。
  • fix:修复问题。
  • docs:文档或内容。
  • test:测试。
  • refactor:不改变行为的重构。
  • chore:构建、依赖、脚手架等维护工作。

提 PR 前自己先走一遍

我的习惯是至少检查四件事:

git status
git diff
git diff --check
npm test

如果是前端或内容站点,再补一个构建:

npm run build

status 确认没有误带文件;diff 让自己先 review 一遍;diff --check 能抓一些空白问题;测试和构建则验证行为没有被改坏。

冲突不要急着“选全部”

遇到冲突时,先确认三件事:

git status
git diff
git log --oneline --left-right --graph HEAD...origin/main

然后逐个文件理解两边改动的意图。很多冲突不是“保留我的”或“保留对方的”这么简单,而是要把两边有效变化重新组合起来。解决后再跑测试,最好也打开相关页面或功能手动看一眼。

保护主分支

团队协作里,mainmaster 应该是相对稳定的基线。比较稳的做法是:

  • 所有改动走 PR。
  • 主分支要求 CI 通过。
  • 至少有一次 review 后再合并。
  • 公共分支不随意 force push。
  • 发布后发现问题优先用 revert,少在公共历史上做手术。

这样做会让流程看起来多一步,但它换来的是更少的“谁把主分支弄坏了”。

一套日常工作流

下面是一套足够通用的工作流,适合多数小团队和个人项目:

git checkout main
git pull --ff-only

git checkout -b feature/git-guide

# 写代码或内容
git status
git diff
git add -p
git commit -m "docs: rewrite git guide"

git fetch origin
git rebase origin/main

npm test
git push -u origin feature/git-guide
gh pr create --base main --draft

这套流程背后的原则很简单:

  • 开始前先基于最新主分支。
  • 每个任务有独立分支。
  • 提交前看 diff。
  • 合并前跑测试。
  • 用 PR 把讨论、CI 和最终合并放到同一个协作入口。

能提高效率的工具和插件

命令行是 Git 的核心,但不代表所有动作都必须硬敲命令。好的工具能减少切换上下文,也能让历史、冲突和差异更可视化。

VS Code 内置 Git 和 GitLens

VS Code 自带 Git 面板已经能完成暂存、提交、查看 diff、处理冲突等基础操作。GitLens 适合进一步看文件历史、行级 blame、提交图和 PR 上下文。它最大的价值不是“替你用 Git”,而是把“这行为什么变成这样”这类问题查得更快。

GitHub CLI

如果项目在 GitHub 上,gh 很适合把 PR 流程放回终端:

gh pr create --base main --draft
gh pr status
gh pr checks
gh pr view --web

它适合已经习惯命令行的人。写完代码后不用离开终端,就能创建 PR、看检查状态、打开 review 页面。

命令缩写和 shell 插件

如果你经常敲 Git 命令,最实用的效率提升反而不是复杂 GUI,而是一组稳定缩写。比如 oh-my-zsh 的 git plugin 就内置了很多常见别名:

gst     git status
gss     git status --short
gaa     git add --all
gapa    git add --patch
gco     git checkout
gcb     git checkout -b
gcmsg   git commit --message
gd      git diff
gds     git diff --staged
gl      git pull
gp      git push
grbi    git rebase --interactive
gstp    git stash pop

缩写的价值不是炫技,而是降低高频动作的摩擦。我的建议是先只记 5 个:gstgcogcbgcmsggp。等它们真的进了手指,再慢慢加 gapagrbi 这种更进阶的命令。

GUI 客户端:GitHub Desktop、Fork、Sourcetree

GUI 客户端适合三类场景:

  • 你想更直观看分支图和提交历史。
  • 你需要逐行暂存、丢弃、对比改动。
  • 你遇到冲突,希望用可视化界面辅助判断。

GitHub Desktop 更轻,适合 GitHub 项目和基础协作;Fork 的 diff、rebase、冲突处理体验比较强;Sourcetree 功能完整,适合喜欢可视化分支图和复杂操作的人。

终端 UI:lazygit 和 tig

如果你喜欢终端,但又不想每次都输入长命令,可以试试 lazygit 或 tig。

lazygit 是一个终端里的 Git 操作界面,适合暂存、提交、分支、stash、push/pull 这些高频动作。tig 更偏仓库浏览器和历史查看器,也能辅助按块暂存。它们都适合“手还在终端里,但想更快看清状态”的时候。

pre-commit

pre-commit 可以把格式化、lint、空白检查、提交信息检查等动作放到 Git hooks 里,在提交前自动执行。

它适合团队统一底线:不要把 trailing whitespace、格式错误、明显 lint 问题留给 code review。code review 应该尽量讨论设计和行为,而不是反复指出机械问题。

Commitizen

如果团队希望提交信息稳定遵循 Conventional Commits,Commitizen 可以用交互式方式生成提交信息,也可以配合 hook 校验格式。它不适合替代思考,但很适合减少“这次前缀到底写什么”的小摩擦。

最后

把 Git 用好,不是记住最多命令,也不是把历史修饰得完美无瑕。

真正有价值的是形成一套稳定习惯:改动前知道自己在哪个分支,提交前看 diff,提交时按意图拆分,推送前跑检查,合并前让 PR 和 CI 兜住风险。这样 Git 就不只是“保存版本”的工具,而是日常开发里帮助你和团队保持清醒的协作基础设施。

参考资料