Git 从入门到进阶:一篇吃透版本管理的实战指南
Git 是现代软件工程里最重要的基础工具之一。它不仅是“保存代码版本”的工具,更是团队协作、代码评审、发布回滚、问题追踪和知识沉淀的基础设施。
很多人学 Git 时会陷入两个误区:一是把命令背成孤立清单,遇到真实问题仍然不知道该用哪个;二是把 GitHub、GitLab、Gitee 这类平台等同于 Git 本身。前者会让操作变得机械,后者会让概念混乱。
一、先分清:Git、远程仓库和托管平台
Git 是一个分布式版本控制系统。它运行在你的本机,负责记录文件快照、提交历史、分支指针和标签。即使没有网络,Git 仍然可以提交、查看历史、创建分支、回滚本地改动。
远程仓库是另一份 Git 仓库副本,通常放在服务器上,用来和团队共享提交。origin只是远程仓库的默认别名,不是固定概念,也可以叫upstream、company或其他名字。
GitHub、GitLab、Gitee、Gitcode等平台是 Git 仓库托管服务。它们在 Git 之上提供 Pull Request、Merge Request、代码评审、CI、权限管理、Issue、Release 等协作能力。
| 名称 | 本质 | 主要作用 |
|---|---|---|
| Git | 本地版本控制工具 | 记录历史、分支、合并、回滚 |
| 远程仓库 | 另一份 Git 仓库副本 | 多人同步提交 |
| GitHub 等平台 | Git 托管与协作平台 | 评审、CI、权限、发布管理 |
二、Git 的四个核心区域
学习 Git 最重要的不是先背命令,而是理解改动在不同区域之间如何流动。
Git 日常操作围绕四个区域展开:
| 区域 | 英文常见说法 | 你需要知道什么 |
|---|---|---|
| 工作区 | Working Tree | 你正在编辑的真实文件 |
| 暂存区 | Staging Area / Index | 下一次提交要包含的改动清单 |
| 本地仓库 | Local Repository | 已提交的历史、分支、标签都在这里 |
| 远程仓库 | Remote Repository | 团队共享的仓库副本 |
最基本的流动关系如下:
工作区--gitadd-->暂存区--gitcommit-->本地仓库--gitpush-->远程仓库从远程获取别人改动时,通常是:
远程仓库--gitfetch /gitpull-->本地仓库和工作区你只要记住一句话:每次不知道下一步怎么做,先执行git status。它会告诉你改动在哪个区域、哪些文件已暂存、当前分支是否落后或领先远程分支。
三、第一次使用 Git:配置与初始化
3.1. 配置身份
Git 的每次提交都会记录作者信息。首次使用建议设置全局用户名和邮箱:
gitconfig--globaluser.name"你的名字"gitconfig--globaluser.email"you@example.com"查看配置:
gitconfig--global--list3.2. 设置默认分支名
现在很多项目默认主分支叫main。你可以设置新仓库的默认分支名:
gitconfig--globalinit.defaultBranch main已经创建过的仓库如果主分支不是main,可以在仓库内改名:
gitbranch-Mmain3.3. 新建一个仓库
在已有项目目录中启用 Git:
gitinitgitstatusgitaddREADME.mdgitcommit-m"docs: 初始化项目说明"如果要关联远程仓库:
gitremoteaddorigin<仓库地址>gitpush-uorigin main-u的意思是建立当前本地分支和远程分支的跟踪关系。设置之后,后续在这个分支上可以直接使用git push或git pull。
3.4. 克隆已有仓库
参与已有项目时,一般从克隆开始:
gitclone<仓库地址>cd<项目目录>gitstatus克隆完成后,你本地会得到完整历史,以及默认远程名origin。
四、最常用的一套日常流程
真实项目里,Git 不应该随手乱用。建议把日常流程固定下来:同步主线、创建分支、修改代码、检查变化、提交、同步、推送、发起评审。
一个标准功能开发流程可以这样做:
# 1. 切回主线gitswitch main# 2. 获取远程最新提交,并要求只能快进合并gitpull --ff-only# 3. 创建功能分支gitswitch-cfeat/login# 4. 修改代码后查看状态gitstatusgitdiff# 5. 挑选要提交的改动gitadd-p# 6. 提交gitcommit-m"feat: 增加短信登录入口"# 7. 推送当前分支,并建立上游跟踪gitpush-uorigin feat/login这里有几个细节值得养成习惯。
第一,尽量不要直接在main上开发。主线分支应该保持稳定,功能、修复、实验都放在独立分支。
第二,提交前先看git diff。很多糟糕提交不是因为 Git 难,而是因为作者没有检查自己到底提交了什么。
第三,优先使用git add -p。它可以按代码块挑选改动,适合把无关变化拆开提交。
第四,推送前先同步主线。如果团队约定使用线性历史,可以在个人分支上执行:
gitfetch origingitrebase origin/main如果团队不使用 rebase,就按团队约定 merge。关键不是某个命令绝对正确,而是团队策略一致。
五、看懂分支:分支不是文件夹
很多初学者会把分支理解成“复制了一份文件夹”。这个理解不够准确。Git 分支本质上只是一个指针,指向某个提交。你每次提交,当前分支指针就向前移动。
常用分支命令:
# 查看本地分支gitbranch# 查看本地和远程分支gitbranch-a# 查看分支跟踪关系gitbranch-vv# 切换分支gitswitch main# 创建并切换到新分支gitswitch-cfeat/login# 删除已合并分支gitbranch-dfeat/login# 强制删除未合并分支,谨慎使用gitbranch-Dfeat/login分支命名建议
分支名不需要复杂,但要能表达目的。常见规范如下:
| 类型 | 示例 | 含义 |
|---|---|---|
feat/ | feat/login | 新功能 |
fix/ | fix/payment-timeout | 缺陷修复 |
hotfix/ | hotfix/order-crash | 紧急线上修复 |
chore/ | chore/update-deps | 构建、依赖、工程事务 |
docs/ | docs/git-guide | 文档 |
一个好分支名应该让评审者不用点开代码,也能大致知道这个分支要解决什么问题。
六、提交不是保存按钮:写好 Commit
git commit不是“我怕代码丢了,所以先保存一下”。提交应该代表一个完整、可理解、可验证的变更单元。
6.1. 好提交的三个标准
第一,单一目的。一次提交只解决一个问题。不要把功能开发、格式化、重构、依赖升级混在一起。
第二,可验证。提交后项目应该尽量保持可构建、可测试。至少不要把明显无法运行的中间状态提交到共享分支。
第三,可回滚。如果这个提交上线后出问题,团队能独立撤销它,而不会误伤一堆无关改动。
6.2. 提交信息怎么写
推荐使用“类型 + 简短说明”的格式:
gitcommit-m"feat: 增加短信登录入口"gitcommit-m"fix: 修复订单金额精度问题"gitcommit-m"refactor: 提取用户权限判断逻辑"gitcommit-m"docs: 更新部署说明"gitcommit-m"test: 补充支付超时用例"常见类型:
| 类型 | 适用场景 |
|---|---|
feat | 新功能 |
fix | 缺陷修复 |
refactor | 不改变外部行为的重构 |
docs | 文档 |
test | 测试 |
chore | 工程配置、依赖、脚本等事务 |
perf | 性能优化 |
style | 格式、空白、样式,不影响逻辑 |
当变更复杂时,不要只写一行标题。可以打开编辑器写多行提交信息:
gitcommit推荐结构:
feat: 增加短信登录入口 当前账号体系只支持密码登录,移动端注册转化率较低。 本次增加短信验证码登录入口,并复用现有验证码发送限流逻辑。 验证方式: - npm test - 手动验证验证码过期提示 关联:#1236.3. 修改最近一次提交
如果刚提交完发现漏了一个文件,且还没有推送,可以使用:
gitadd<文件>gitcommit--amend如果只是改提交信息:
gitcommit--amend-m"fix: 修正验证码过期提示"注意:--amend会产生新的提交哈希。如果提交已经推送到多人共享分支,不要随意 amend。
七、Merge 与 Rebase:两种整合历史的方式
分支开发完成后,需要把改动整合回主线。常见方式是 merge 和 rebase。
7.1. Merge:保留真实分叉历史
gitswitch maingitmerge feat/loginmerge 的特点是不会改写已有提交。它会把两条历史线合并起来,必要时生成一个合并提交。优点是历史真实、审计友好;缺点是分支多时提交图可能比较复杂。
适合场景:
- 主线集成功能分支。
- 多人共同维护同一个长期分支。
- 团队希望保留完整分叉与合并记录。
7.2. Rebase:把提交重放到新基底上
gitfetch origingitrebase origin/mainrebase 的特点是会改写提交哈希。它把当前分支上的提交拿出来,放到新的基底后面重新应用。优点是历史线性、评审清晰;风险是如果别人已经基于你的旧提交继续开发,改写历史会影响他们。
适合场景:
- 整理自己的本地功能分支。
- 提交 PR 前让分支跟上最新主线。
- 团队明确约定使用线性历史。
重要原则:
可以 rebase 自己尚未共享的分支。 不要随意 rebase 已经被他人基于开发的共享分支。7.3. Pull 到底做了什么
git pull本质上相当于:
gitfetchgitmerge如果使用:
gitpull--rebase则相当于:
gitfetchgitrebase很多团队会要求使用git pull --ff-only,它只允许快进合并。如果本地和远程出现分叉,命令会失败,提示你先明确选择 merge 或 rebase。这种方式能避免不小心生成意外的合并提交。
八、冲突处理:不要害怕,但要认真
冲突不是 Git 出错,而是 Git 无法自动判断最终应该保留哪段内容。比如你和同事都修改了同一行,Git 不知道哪一边代表正确业务语义。
冲突处理流程:
# 1. 查看冲突文件gitstatus# 2. 打开文件,找到冲突标记<<<<<<<HEAD 当前分支中的内容=======对方分支中的内容>>>>>>>origin/main# 3. 手动编辑为最终正确结果,并删除冲突标记# 4. 标记已解决gitadd<文件># 5. 如果是 mergegitcommit# 6. 如果是 rebasegitrebase--continue如果 rebase 过程中发现方向错了,可以中止:
gitrebase--abort如果 merge 过程中想回到合并前状态:
gitmerge--abort处理冲突时最危险的做法,是为了“让冲突消失”而直接选择某一边。正确做法是理解双方改动目的,必要时找相关同事确认,再保留真正正确的最终代码。
九、撤销与回滚:先判断改动在哪个阶段
Git 撤销命令很多,真正难点不是命令本身,而是判断当前改动处于哪个阶段,以及是否已经推送到共享分支。
9.1. 丢弃工作区改动
适用于文件已经修改,但还没有暂存:
gitrestore<文件>丢弃当前目录下已跟踪文件的工作区改动要非常谨慎:
gitrestore.执行前建议先看:
gitdiff9.2. 从暂存区撤出
适用于已经git add,但不想让某些文件进入下一次提交:
gitrestore--staged<文件>这不会丢弃文件内容,只是把改动从暂存区拿回工作区。
9.3. 撤销本地未推送提交
如果最近一次提交还没有推送,可以根据目的选择:
# 撤销提交,但保留改动在暂存区gitreset--softHEAD~1# 撤销提交,保留改动在工作区gitreset--mixedHEAD~1# 撤销提交,并丢弃对应工作区改动,危险gitreset--hardHEAD~1git reset --hard是高风险命令。它会移动分支指针,并让工作区回到目标提交状态。执行前必须确认没有未保存的重要改动。
9.4. 回滚已经推送的提交
如果提交已经进入共享分支,优先使用revert:
gitrevert<提交哈希>revert会生成一个新的反向提交,用来抵消指定提交的影响。它不会改写公共历史,因此更适合团队协作和审计。
9.5. 找回误删提交
如果你误删分支、reset 错了,先别慌。Git 可能还能通过 reflog 找到旧位置:
gitrefloggitswitch-crescue<提交哈希>reflog是本地引用日志,记录 HEAD 和分支指针曾经指向哪里。它不是永久备份,过期清理后可能消失,所以越早处理越好。
十、进阶命令:提升效率与排障能力
当你掌握基础流程后,这些命令会显著提升真实项目效率。
10.1. Stash:临时保存现场
当你正在开发一半,但需要马上切换分支修一个线上问题,可以先 stash:
gitstash push-u-m"登录页开发到一半"说明:
-u会包含未跟踪文件。- 默认不会包含被
.gitignore忽略的文件。 -m给这次 stash 加说明,方便以后识别。
查看 stash:
gitstash list恢复并删除最近一次 stash:
gitstash pop恢复但保留 stash 记录:
gitstash apply10.2. Cherry-pick:拣选某个提交
当你只想把另一个分支上的某个提交拿过来:
gitcherry-pick<提交哈希>它会把该提交的补丁应用到当前分支,并生成一个新的提交,因此新提交哈希和原提交通常不同。
适合场景:
- 把修复提交同步到 release 分支。
- 从实验分支挑一个稳定改动。
- 临时补齐某个环境缺失的提交。
10.3. Tag:标记发布版本
标签常用于标记发布点:
gittag-av1.2.0-m"发布 v1.2.0"gitpush origin v1.2.0查看标签:
gittag查看某个标签信息:
gitshow v1.2.0建议生产发布使用带说明的注解标签,即git tag -a。
10.4. Bisect:二分定位问题提交
当你知道“某个旧版本是好的,现在坏了”,但不知道哪个提交引入问题,可以用 bisect:
gitbisect startgitbisect badgitbisect good<旧提交哈希>Git 会切到中间提交,你运行测试或手动验证后告诉它:
gitbisect good# 或gitbisect bad重复几轮后,Git 会定位到最可能引入问题的提交。结束后:
gitbisect reset10.5. Worktree:同时检出多个分支
如果你经常需要在功能开发和线上修复之间切换,git worktree比频繁 stash 更舒服:
gitworktreeadd../project-hotfix hotfix/payment这样你可以在另一个目录同时打开 hotfix 分支,而原目录继续保留当前开发现场。
查看 worktree:
gitworktree list删除不再使用的 worktree:
gitworktree remove../project-hotfix十一、远程协作:PR / MR 是质量闸门
团队项目中,远程仓库不是简单备份。它连接了代码评审、自动化测试、权限控制、版本发布和回滚审计。
一个成熟 PR 应该包含:
- 背景:为什么要改。
- 变更:改了哪些核心内容。
- 验证:跑了哪些测试,手动验证了哪些路径。
- 风险:是否涉及兼容性、数据迁移、性能或安全。
- 截图:前端或可视化改动应提供前后对比。
示例 PR 描述:
## 背景 登录页目前只支持密码登录,移动端转化率低。 ## 变更 - 增加短信验证码登录入口 - 复用已有验证码限流逻辑 - 增加验证码过期提示 ## 验证 - npm test - 手动验证验证码发送、过期、重试路径 ## 风险 - 依赖短信服务可用性 - 需要观察发送失败率常见远程命令
查看远程仓库:
gitremote-v获取远程最新状态,但不自动合并:
gitfetch origin删除本地已经失效的远程跟踪分支:
gitfetch--prune推送当前分支并建立跟踪关系:
gitpush-uorigin<分支名>删除远程分支:
gitpush origin--delete<分支名>十二、.gitignore、LFS 与仓库治理
仓库治理影响长期可维护性。忽略规则、大文件策略和密钥管理如果做得不好,仓库会越来越慢,甚至引发安全事故。
12.1. .gitignore 只影响未跟踪文件
常见.gitignore:
# 依赖 node_modules/ # 构建产物 dist/ build/ # 日志 *.log # 本地环境 .env .env.local # 系统文件 .DS_Store Thumbs.db一个常见误区是:把文件写进.gitignore后,Git 就不再跟踪它。实际上,如果文件已经被提交过,.gitignore不会让它自动消失。
停止跟踪但保留本地文件:
gitrm--cached<文件>gitcommit-m"chore: 停止跟踪本地配置文件"12.2. 用 .gitattributes 管理换行和二进制文件
跨平台团队容易遇到 CRLF 和 LF 换行差异。更稳妥的做法是在仓库中使用.gitattributes明确规则,而不是依赖每个人的全局配置。
示例:
* text=auto *.sh text eol=lf *.bat text eol=crlf *.png binary *.jpg binary个人的core.autocrlf应遵循团队约定。项目内有.gitattributes时,优先以项目规则为准。
12.3. 大文件使用 Git LFS
Git 擅长管理源码文本,不擅长频繁变化的大型二进制文件。图片源文件、模型、设计稿、压缩包等大文件可以考虑 Git LFS:
gitlfsinstallgitlfs track"*.psd"gitadd.gitattributesgitadddesign.psdgitcommit-m"chore: 使用 LFS 管理设计源文件"注意:LFS 对后续新增文件有效。已经进入历史的大文件,需要专门做历史迁移,不能简单认为git lfs track会自动瘦身旧历史。
12.4. 密钥泄露必须轮换
如果.env、私钥、Token 被提交过,正确处理不是只删掉文件。只要进入过 Git 历史,就可能被别人拉取过。
严谨处理步骤:
- 立即撤销或轮换泄露的密钥。
- 从当前代码中删除密钥。
- 必要时用
git filter-repo或 BFG 等工具清理历史。 - 通知团队重新拉取或按迁移方案处理。
- 增加扫描工具,避免再次提交。
安全原则很简单:提交过的密钥视为已经泄露。
十三、故障排查:不要先复制命令
Git 出问题时,最重要的是先定位问题范围。盲目复制reset --hard、force push之类命令,往往会把小问题变成团队事故。
排查三件套:
gitstatusgitdiffgitlog--oneline--graph--decorate-n12如果涉及远程:
gitbranch-vvgitremote-vgitfetch origin如果怀疑提交丢失:
gitreflog常见场景处理
场景 1:我改乱了一个文件,想恢复到上次提交
gitrestore<文件>场景 2:我 add 错了文件
gitrestore--staged<文件>场景 3:刚提交完发现漏了文件
gitadd<文件>gitcommit--amend前提是还没有推送到共享分支。
场景 4:提交已经推送,想撤销它
gitrevert<提交哈希>这是公共历史中更稳妥的做法。
场景 5:本地分支和远程主线分叉了
先获取远程状态:
gitfetch origin如果是个人功能分支,团队也允许线性历史:
gitrebase origin/main如果团队保留合并记录:
gitmerge origin/main场景 6:我把分支删了
先查 reflog:
gitreflog找到旧提交后创建救援分支:
gitswitch-crescue<提交哈希>场景 7:远程分支已经删了,本地还看得到
gitfetch--prune这会清理失效的远程跟踪分支。
十四、常用命令速查
下面这张图适合收藏,也适合放到团队 Wiki。
补充一个更完整的命令表:
| 目的 | 命令 | 说明 |
|---|---|---|
| 查看状态 | git status | 查看当前分支、工作区、暂存区 |
| 查看未暂存差异 | git diff | 对比工作区和暂存区 |
| 查看已暂存差异 | git diff --staged | 对比暂存区和最近提交 |
| 暂存文件 | git add <文件> | 加入下一次提交 |
| 分块暂存 | git add -p | 按块选择改动 |
| 提交 | git commit -m "说明" | 生成提交 |
| 修改最近提交 | git commit --amend | 改写最近一次提交 |
| 查看历史 | git log --oneline --graph | 图形化简洁历史 |
| 查看提交详情 | git show <提交哈希> | 查看某次提交内容 |
| 查看文件归属 | git blame <文件> | 查看每行最后修改者 |
| 创建分支 | git switch -c <分支> | 创建并切换 |
| 切换分支 | git switch <分支> | 切到已有分支 |
| 合并分支 | git merge <分支> | 合并指定分支 |
| 变基 | git rebase <基底> | 把当前提交重放到新基底 |
| 获取远程 | git fetch origin | 只获取,不合并 |
| 拉取并快进 | git pull --ff-only | 只允许快进 |
| 推送 | git push | 推送当前分支 |
| 推送并跟踪 | git push -u origin <分支> | 建立上游关系 |
| 撤出暂存 | git restore --staged <文件> | 暂存区回到工作区 |
| 丢弃工作区改动 | git restore <文件> | 恢复为最近提交状态 |
| 公共回滚 | git revert <提交哈希> | 新增反向提交 |
| 本地重置 | git reset --soft HEAD~1 | 撤销提交,保留暂存 |
| 临时保存 | git stash push -u -m "说明" | 保存当前现场 |
| 找回历史位置 | git reflog | 查看本地引用移动记录 |
十五、团队 Git 规范建议
个人会用 Git 只是第一步,团队要靠规范降低协作成本。下面是一套可落地的建议。
15.1. 主线保护
主线分支建议开启保护:
- 禁止直接 push。
- 必须通过 PR / MR。
- 必须通过自动化测试。
- 至少一名评审者批准。
- 对高风险仓库启用 CODEOWNERS 或类似机制。
15.2. 分支生命周期要短
功能分支存在越久,越容易和主线偏离,冲突风险越高。建议:
- 小需求 1 到 3 天内合入。
- 大需求拆成多个可评审 PR。
- 长期分支定期同步主线。
- 合并后及时删除远程分支。
15.3. 提交历史保持可读
推荐:
- 一次提交一个意图。
- 提交信息说明“为什么”。
- 无关格式化单独提交。
- 不提交临时代码、调试日志、个人配置。
- 推送前用
git log --oneline --graph -n 12看一眼历史。
15.4. 危险命令建立团队约束
以下命令不是不能用,而是要清楚后果:
| 命令 | 风险 |
|---|---|
git reset --hard | 丢弃工作区和暂存区改动 |
git push --force | 改写远程历史,可能影响他人 |
git clean -fd | 删除未跟踪文件 |
git rebase | 改写提交哈希,不适合随意用于共享分支 |
如果确实需要强推,优先使用带保护的方式:
gitpush --force-with-lease它会在远程分支没有被别人更新时才允许强推,比--force更稳妥,但仍然需要遵守团队约定。
15.5. 让自动化替你守门
Git 工作流最好和自动化检查结合:
- 提交前运行格式化和测试。
- PR 自动运行 CI。
- 合并前检查测试、Lint、类型检查、构建。
- 对密钥、许可证、大文件进行扫描。
- 发布时用 tag 和 release note 记录版本。
十六、一套推荐的新手练习路径
如果你刚开始学 Git,不建议只看教程。最好拿一个临时目录练习下面这些场景:
git init创建仓库,提交 README。- 修改同一个文件,分别练习
git diff、git add、git commit。 - 创建两个分支,分别提交,再 merge。
- 人为制造冲突,练习解决冲突。
- 使用
git restore丢弃工作区改动。 - 使用
git restore --staged撤出暂存。 - 使用
git reset --soft撤销未推送提交。 - 使用
git revert回滚一个提交。 - 使用
git stash临时保存现场。 - 使用
git reflog找回一个旧提交。
练习时不要怕把仓库弄坏。真正的能力来自你知道弄坏后如何恢复。
十七、最终心法
Git 的难点不是命令数量,而是历史意识。你要始终知道三件事:
第一,当前改动在哪个区域:工作区、暂存区、本地仓库,还是远程仓库。
第二,当前操作是否会改写历史:commit --amend、reset、rebase都会产生新的提交关系。
第三,这段历史是否已经共享给别人:只要进入共享分支,就优先选择保留审计痕迹的修复方式,例如revert。
最后记住这条原则:
本地私有历史可以整理,远程共享历史要谨慎。 先看 status,再做决定。如果你能真正理解这两句话,Git 就不再是一堆吓人的命令,而会变成一套帮助你管理变化、协作和风险的工程系统。