拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Git 版本控制完全指南:从入门到团队协作

前言不管你是刚写代码的学生还是刚入职的应届生“Git” 这个词你一定听过。但真正用它的时候很多人会陷入会敲命令但不知道发生了什么的困境——每次 merge 冲突就慌rebase 不知道什么时候该用reset 之后 commit 丢了不知道怎么找回来。这篇文章的目标就是让你从零认知到真正理解 Git一步一步跟着做踩过的坑提前告诉你解决方法。读完这篇你应该能自信地在团队项目中使用 Git 了。一、Git 核心概念四个区域的关系很多人学 Git 卡在第一步就是没搞懂工作区“暂存区”“本地仓库”远程仓库这四个东西到底是什么、之间的关系是什么。搞懂这个后面的命令都是顺水推舟。1.1 四个区域是什么工作区Working Directory / Working Tree就是你电脑硬盘上那个项目文件夹。你用 VS Code 打开的目录、在里面修改文件的操作都在工作区完成。工作区的文件有两种状态已跟踪之前被 Git 记录过和未跟踪新建的文件Git 还不知道它的存在。暂存区Staging Area / Index也叫索引。这是一个中间地带你可以理解为即将提交的预备队列。git add命令就是把工作区的改动放进暂存区git commit才会把暂存区的东西正式写入本地仓库。本地仓库Local Repository.git目录就是你的本地仓库。它保存了项目的完整历史记录——所有 commit、分支、标签都在这里。本地仓库在你自己的电脑上不需要联网。远程仓库Remote Repository托管在服务器上的 Git 仓库GitHub、GitLab、Gitee 都是远程仓库。远程仓库是团队成员共享代码的地方。你的本地仓库通过push、pull、fetch与远程仓库同步。1.2 四区域关系图┌─────────────────────────────────────────────────────────────────┐ │ 远程仓库 (Remote) │ │ 例如: GitHub / GitLab / Gitee 上的项目仓库 │ └─────────── push ──────────── pull/fetch ───────────────────────┘ ▲ │ │ ▼ ┌──────────────┴──────────────┐ │ 本地仓库 (Local) │ │ .git/ 目录记录完整历史 │ │ HEAD 指针指向当前 commit │ └────────── commit ────────────┘ ▲ │ git commit ┌──────────────┴──────────────┐ │ 暂存区 (Staging Area) │ │ git add 后的文件等待提交 │ └─────────── add ──────────────┘ ▲ │ ┌──────────────┴──────────────┐ │ 工作区 (Working Dir) │ │ 你实际编辑代码的地方 │ │ .git 所在的最外层目录 │ └──────────────────────────────┘数据流向git add→ 工作区 → 暂存区git commit→ 暂存区 → 本地仓库git push→ 本地仓库 → 远程仓库git pull / fetch→ 远程仓库 → 本地仓库记住一个原则只有进入暂存区git add的文件commit 才会包含它。这是新人最容易踩的坑——改了半天代码忘记 addcommit 了发现什么都没变。1.3 文件的三种状态每个文件在 Git 中必然处于三种状态之一已修改Modified文件被改了但还没放入暂存区已暂存Staged文件被 add 了已放入暂存区等待 commit已提交Committed文件已 commit写入本地仓库这三种状态对应了工作区→暂存区→本地仓库的流转过程。二、基础操作Git 的日常三板斧本节覆盖 Git 最常用的几个命令足够应付日常开发。2.1 初始化仓库git init如果你有一个新项目想用 Git 管理进入项目目录执行cdmy-projectgitinit执行后会发现目录里多了一个.git文件夹这就是你的本地仓库。不要手动去改这个文件夹里的内容。如果你想从远程仓库克隆一份代码下来用gitclone https://github.com/username/repo.gitgit clone会自动把远程仓库绑定为origin不需要再手动添加 remote。2.2 查看状态git status这是你每天要用最多的命令。每次操作前先看一下状态养成习惯。gitstatus输出会告诉你哪些文件被修改了Modified哪些文件在暂存区等待提交Changes to be committed哪些新文件还没被跟踪Untracked files一个典型的状态输出On branch main Changes not staged for commit: (use git add file... to update what will be committed) modified: src/app.py Untracked files: (use git add file... to include in what will be committed) newfile.txt2.3 暂存git addgit add有几种常见用法# 添加单个文件gitaddapp.py# 添加所有改动的文件不包括未跟踪的新文件gitadd-u# 添加所有文件包括新文件gitadd.# 添加所有文件gitadd-A建议不要直接git add .用git add -p可以逐个查看每个改动块hunk选择性暂存避免把不相关的修改一起提交gitadd-p2.4 提交git commit# 普通提交gitcommit-m修复登录页面样式问题# 一次性 add commit不推荐绕过了逐个确认的过程gitcommit-am修复bugcommit message 规范用中文写清楚这次做了什么。一个好的 commit message 应该让其他人或三个月后的你自己一眼就知道这次提交的目的。常见格式类型: 简短描述 [可选的详细说明]类型可以是feat新增功能、fix修复 bug、docs文档改动、refactor重构、test测试等。2.5 查看历史git log# 查看完整提交历史按 Q 退出gitlog# 简洁版一行一个 commitgitlog--oneline# 图形化显示分支很实用gitlog--oneline--graph# 查看最近 5 次提交gitlog-5git log --oneline --graph的输出大概长这样* a1b2c3d 修复支付回调 bug * d4e5f6g 添加用户注册功能 * h8i9j0k 初始化项目结构2.6 对比差异git diff# 对比工作区和暂存区还没 add 的改动gitdiff# 对比暂存区和最新一次 commitadd 了之后想看看要提交什么gitdiff--cached# 对比两个 commit 的差异gitdiffabc1234..def5678# 对比当前分支和另一个分支gitdiffmain..feature-login三、分支操作branch、checkout、merge、rebase分支是 Git 最强大的功能之一。团队协作中几乎所有新功能都应该在新分支上开发而不是直接在 main/master 分支上改。3.1 创建和查看分支# 查看本地分支gitbranch# 查看所有分支包括远程的gitbranch-a# 创建新分支但不切换过去gitbranch feature-login3.2 切换分支git checkout / git switch# 切换到已有分支gitcheckout feature-login# 创建并切换到新分支一句话搞定更常用gitcheckout-bfeature-login# Git 2.23 推荐用 switch更直观gitswitch feature-login# 切换到已有分支gitswitch-cfeature-login# 创建并切换切换分支前确保工作区是干净的没有未提交的改动否则 Git 会阻止你切换防止把未保存的改动带到其他分支。如果确实需要切换用git stash先把改动暂存起来后面会讲。3.3 合并分支git merge当你完成了一个功能想把它合并回主分支时# 先切换到目标分支你要合并到哪个分支gitswitch main# 把功能分支合并进来gitmerge feature-login合并有两种情况情况一快进合并Fast Forward如果 main 分支没有被新的 commit 更新过Git 直接把 main 的指针快进到 feature-login 所在的 commit就像这样合并前 main: A--B--C \ feature: D--E 合并后 main: A--B--C--D--E 指针快进到 E这是最理想的情况没有冲突。情况二三方合并Three-way merge如果 main 在你开发期间也有新的 commitGit 需要把两条分支的历史合并起来合并前 main: A--B--C--F \ feature: D--E 合并后 main: A--B--C--F---G \ / feature: D---E G 是新的 merge commit记录了合并这种情况有时会产生合并冲突需要手动解决后文会讲。3.4 变基git rebaserebase和merge都能把一个分支的改动整合到另一个分支但方式完全不同。rebase 的原理把当前分支上的 commit摘下来重新放到目标分支的最新 commit 之上。打个比方merge 是把两条河流汇合rebase 则是把一条分支嫁接到另一条分支的末端。# 假设你在 feature 分支上想把它的改动基于最新的 main 分支gitswitch feature-logingitrebase main执行过程rebase 前 main: A--B--C--F \ feature: D--E--G rebase 后 main: A--B--C--F \ feature: (D--E--G) ← 新的 commitcommit hash 变了 ↑ 基于 F 的新位置3.5 merge vs rebase什么时候用哪个这是新手最容易纠结的问题。场景推荐方式原因整理自己分支上的本地 commitrebase -i让 commit 历史更清晰把功能分支合并回主分支merge保留完整历史不改变已有 commit同步主线更新到功能分支rebase保持分支干净提交历史线性公共分支main/master永远用 merge不要 rebase 公共分支会影响其他人冲突很多时mergerebase 逐个 commit 解决冲突会很痛苦一个简单原则在个人分支上整理历史 → rebase合并回主分支 → merge公共分支永远不要 rebase3.6 整理本地 commit交互式 rebase如果你的本地分支上有多个零碎的 commit想合并成几个清晰的提交# 把最近 3 个 commit 整理成更清晰的提交gitrebase-iHEAD~3会打开一个文本编辑器显示最近 3 个 commitpick abc1234 添加用户模块 pick def5678 修复样式bug pick hij9012 更新文档 # Commands: # p, pick use commit # r, reword use commit, but edit the commit message # s, squash use commit, meld into previous commit # f, fixup like squash, but discard this commits message # d, drop remove commit把第二个和第三个 commit 前面的pick改成squash或s保存退出后Git 会让你写一个新的 commit message。这种方式让你的提交历史更干净、更专业。四、远程协作remote、push、pull、fetch4.1 绑定远程仓库git remote克隆下来的仓库已经自动绑定了origin。如果是手动初始化的仓库需要手动添加# 查看已绑定的远程仓库gitremote-v# 添加远程仓库gitremoteaddorigin https://github.com/username/repo.git# 修改远程仓库地址gitremote set-url origin https://github.com/username/repo-new.gitorigin只是默认的命名约定你可以叫任何名字比如upstream。4.2 上传代码git push# 推送本地分支到远程首次推送需要设置上游分支gitpush-uorigin feature-login# 之后就可以简写gitpush# 推送所有分支gitpush--all# 删除远程分支本地分支还在gitpush origin--deleteold-branch-u或--set-upstream的作用是把本地分支和远程分支关联起来之后 push 和 pull 就不用每次指定分支名了。4.3 下载代码pull vs fetch这两个命令都从远程拉代码但行为完全不同git fetch只把远程仓库的更新下载到本地不合并到工作区。执行后你可以在本地查看远程分支的改动但你的工作区代码不会变。gitfetch origin# 现在你可以查看 origin/main 和本地 main 的差距了gitlog main..origin/maingitdiffmain..origin/maingit pull等价于git fetchgit merge。把远程更新下载下来然后自动合并到当前分支。gitpull origin main什么时候用什么远程分支比较稳定你想先看看有什么变化 →git fetch 审查确定要合并进来 →git pull团队协作中推荐先用git fetch查看远程更新确认没问题再git pull避免意外的合并 commit 污染历史五、踩坑解决常见问题及处理方法5.1 Merge 冲突怎么解决冲突发生在两个分支修改了同一行代码Git 无法自动合并时。冲突的表现gitmerge feature-login# Auto-merging app.py# CONFLICT (content): Merge conflict in app.py# Automatic merge failed; fix conflicts and then commit the result.打开冲突文件会看到这样的标记HEADdefcalculate():return100defcalculate():return200feature-login解决步骤用编辑器打开文件删除冲突标记、、保留你想保留的代码或合并两段代码git add暂存解决后的文件git commit完成合并Git 会自动生成 merge commit message避免冲突的建议小步提交频繁和主分支同步同一个文件不要让多个人同时改合并前先git fetch看看远程有什么变化5.2 回退版本git resetgit reset有三个模式控制回退的程度# 软回退保留改动在暂存区工作区不变gitreset--softHEAD~1# 混合回退默认保留改动在工作区不在暂存区gitreset HEAD~1# 硬回退彻底丢弃改动工作区也恢复危险gitreset--hardHEAD~1使用场景你刚 commit 了但发现漏了一个文件 →git reset --soft HEAD~1然后补上漏的文件再 commit你想取消最近的 3 个 commit但保留工作区的改动 →git reset --mixed HEAD~3你想把分支彻底回退到某个版本不想要后来的所有改动 →git reset --hard commit-hash5.3 撤销公共 commitgit revertgit reset是本地操作不应该用在已经 push 到远程的 commit 上。如果要撤销已经推出去的 commit用git revert# 创建一个新的 commit用来撤销指定 commit 的改动gitrevert abc1234# 会打开编辑器让你写 revert 的 commit message保存退出即可gitpushrevert不会删除历史 commit而是生成一个新的 commit 来反做那些改动安全地撤销远程分支上的错误 commit。5.4 rebase 中途失败或出错如果在 rebase 过程中遇到冲突Git 会停下来让你解决冲突。解决完后gitadd.gitrebase--continue如果中途想放弃整个 rebase回到 rebase 开始前的状态gitrebase--abort5.5 丢失的 commit 怎么找回用git reset --hard之后发现回退多了以前的 commit 不见了——别慌Git 没有真的删掉它只是 ref 丢了。# 查看所有分支的操作日志gitreflog输出类似a1b2c3d HEAD{0}: reset: moving to HEAD~2 d4e5f6g HEAD{1}: commit: 添加新功能 f7g8h9i HEAD{2}: commit: 修复bug找到了之前的 commit hash 后直接切换回去gitcheckout d4e5f6g# 查看gitreset--hardd4e5f6g# 彻底恢复git reflog能救命强烈建议每个开发者都记住这个命令。5.6 暂存工作进度git stash当你正在改代码突然需要切换分支处理紧急事情但当前的改动还没好不想 commit 也不想丢失# 暂存当前所有改动gitstash# 查看暂存列表gitstash list# 恢复最新一次暂存保留暂存记录gitstash pop# 恢复最新一次暂存删除暂存记录gitstash apply# 恢复指定的一个 stashgitstash apply stash{2}# 删除某个 stashgitstash drop stash{0}六、团队协作Git 工作流程6.1 Git FlowGit Flow 是一套经典的多分支管理流程适合有固定发布周期的大型项目如 App、桌面软件。master ────●──────────────────●─────────────● (正式发布版本) \ / \ \ / \ feature/a ────●──● │ │ \ \ develop ──────●──●──●──●──●──●──●──●──●──●──● (开发主线) \ / release ────────────●──────● (发布准备分支)核心分支main只保留正式发布版本永远是可部署状态develop集成了所有已完成功能的主开发线feature/*从 develop 拉出的功能分支完成后合并回 developrelease/*从 develop 拉出用于发布前的测试和修复完成后同时合并到 main 和 develophotfix/*线上紧急 bug 修复从 main 拉出修复后合并回 main 和 develop适用场景有明确的版本发布计划、多人在同一项目上协作、需要区分开发版本和正式版本。6.2 Trunk-Based Development主干开发这是近年来更流行的方式适合快速迭代的互联网项目。trunk/main ──●──●──●──●──●──●──●──●──●──●──● \ \ \ feat1 ● ● feat2 ● \ \ feat3 ● ●核心规则所有开发者频繁地把代码合并到 main/trunk至少每天一次功能用短生命周期的 feature 分支最好 1-2 天内完成合并不需要长期存在的 develop 分支通过 feature flag功能开关在合并后控制新功能是否对外可见适用场景持续交付/部署、快速迭代的小团队、CICD 流程成熟的开发团队。6.3 怎么选择Git FlowTrunk-Based团队规模中大型团队小型团队发布节奏有固定版本周期持续交付复杂度分支类型多流程复杂规则简单适用项目传统软件、App互联网产品、微服务对于大多数国内互联网团队尤其是创业公司和小团队Trunk-Based 更实用。但无论选哪种流程核心原则是一样的保持主分支稳定、频繁集成、代码审查Code Review是必须环节。七、常用命令速查表初始化与配置gitinit# 初始化仓库gitcloneurl# 克隆远程仓库gitconfig--globaluser.name你的名字gitconfig--globaluser.email你的邮箱基础操作gitstatus# 查看状态gitaddfile# 暂存文件gitadd-p# 交互式暂存逐块确认gitcommit-mmessage# 提交gitcommit-ammessage# add commit仅限已跟踪文件gitlog--oneline--graph# 简洁图形化历史gitdiff# 工作区 vs 暂存区gitdiff--cached# 暂存区 vs 最新 commit分支操作gitbranch# 查看本地分支gitbranch-a# 查看所有分支gitcheckout-bbranch# 创建并切换分支gitswitchbranch# 切换分支gitmergebranch# 合并分支到当前分支gitrebasebranch# 变基到目标分支gitrebase-iHEAD~3# 交互式整理最近3个 commit远程协作gitremote-v# 查看远程仓库gitpush-uoriginbranch# 首次推送并设置上游gitpush# 推送gitfetch# 获取远程更新不合并gitpull# 拉取并合并gitpull--rebase# 拉取并变基避免多余 merge commit撤销与修复gitreset--softHEAD~1# 软回退保留暂存gitreset--mixedHEAD~1# 混合回退保留工作区gitreset--hardHEAD~1# 硬回退丢弃改动gitrevertcommit-hash# 撤销已推送的 commitgitrebase--abort# 放弃 rebasegitrebase--continue# 继续 rebasegitstash# 暂存当前改动gitstash pop# 恢复并删除 stashgitreflog# 查看操作日志救命命令八、作者的经验总结写完这篇教程最后说几句真心话。关于 Git 本身Git 不是一个需要精通才能用的工具。理解核心概念区域、状态、分支后常用的就是那几个命令。真正的高手不是记住了几十个参数而是知道什么时候用什么命令以及出了问题怎么恢复。关于命令操作永远先git status再操作。很多人踩坑就是因为不确认当前状态就一顿敲。git status不花钱不会破坏任何东西但能帮你避免很多低级错误。关于 commitCommit 应该是一个完整的功能或修复不要把一堆不相干的改动塞进一个 commit。“一个 commit 只做一件事”这个原则让你的项目历史可读、可追溯、可回退。关于团队协作Git 只是一个工具真正决定协作效率的是团队的流程和沟通。再好的 Git 流程如果没有人做 Code Review、没有人维护分支规范也会形同虚设。工具是辅助协作才是核心。关于出问题时Git 几乎不会真的丢失数据。只要.git目录还在所有历史都在那里。遇到问题不要慌git reflog和git status能帮你解决 90% 的问题。希望这篇文章对你有帮助。如果有任何疑问欢迎在评论区留言。祝你的代码永不丢失merge 从无冲突。原创不易转发请注明出处。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门