速成课 · No. 17
Git 记录你项目的每一个版本,让整个团队改动同一批文件而不会互相覆盖。围绕它的恐惧几乎全部来自陌生的词——commit、branch、rebase——而不是真正的难度。学会那寥寥几个术语,Git 就从一个让人发怵的东西,变成你拥有过的最让人安心的安全网。
21 个概念 · 每个都配图示 · 结尾附速查表
在命令之前,先看问题。一旦你感受到版本控制消除的那种痛,Git 的每一个特性读起来就都是显而易见的答案,而不是莫名其妙的术语。
地图:六组共 21 个概念
一张地铁线路图:先看到所有线路的全貌,再一站一站地坐过去。
先讲基础——什么是版本控制、Git 和 repository。然后每组一章:commit(快照是怎么保存的)、branch(并行的工作线)、merge 与 conflict、remote 与团队协作,以及 history 与安全的撤销。最后一章讲好习惯、关系图和速查表。
怎么读每个概念
一本护照:每个人的栏目都一样,所以很容易比较。
从这里往后,每个概念都有相同的几部分:它的英文名称(记住它——这是你查文档和 Git 报错时要搜的词)、一幅来自日常生活的画面、一张图示,以及一张卡片:是什么、怎么运作、一个例子(常带命令)、注意点,以及它与其他概念的关联。结尾是关系图和速查表。
Version Control — 用它代替塞满 _final_v2_FINAL 文件的文件夹
一张桌子被 report.doc、report_v2.doc、report_final.doc、report_final_FINAL.doc 淹没——没人确定哪个是最新的,而周二那个好版本永远丢了。
人人都经历过手动留版本的方式:复制文件,加个后缀,碰运气。它瞬间就崩了——你分不清哪个最新,找不回被覆盖的版本,两个人一起编辑就意味着其中一个的工作会丢。版本控制正是为终结这一切而生的工具:一个项目,它的全部历史被安全保存,每一个版本都可以找回。
- 问题
- 带后缀的副本:不知道哪个最新,旧的没了,两个人互相覆盖。
- 思路
- 一个项目 + 完整的修改历史,任何版本都能恢复。
- 例子
不再有
report_final_FINAL.doc——只有一个report.doc,加上它每个版本的记录。- 注意
- 只有把修改存进系统里才管用,而不是存在系统之外。
- 关联
- Git 是最流行的版本控制系统。它把历史保存在 Repository 里。
Git — 一条快照时间线
一台相机,每次你说「保存这个」就把你整个项目拍下来——于是你可以翻回去,看到过去任意时刻一切的模样。
Git 是几乎人人都在用的版本控制系统。它把你的项目记录成一连串 snapshot——每一个都是你选择保存的那一刻所有文件的完整画面。结果是一条你可以穿行的时间线:看到什么变了、何时变的、由谁改的,并回到任意过去的状态。它是一个永远不会忘记任何一次保存的保存按钮。
- 是什么
- 使用最广泛的版本控制系统。
- 怎么运作
- 把项目存成一条时间线,上面是你选定时刻所有文件的 snapshot。
- 例子
- 快照 3 加入了
style.css;一个月后,你可以精确回到快照 2。 - 注意
- 吓人的是词,而不是难度:术语只是需要学一学。
- 关联
- 每个快照就是一个 Commit;时间线存放在 Repository 里。
Repository — 项目加上它的全部历史
一本书把它曾经的每一版草稿都装订在书后——你手里既有当前的文字,也有它如何变成这样的完整记录。
repository(或 repo)就是被 Git 跟踪之后的项目文件夹:当前的文件加上每一次快照的隐藏历史(它存在一个隐藏的 .git 文件夹里)。clone 一个 repo,你得到的不只是今天的代码,而是完整的历史。它是你工作的基本单位——repo 就是「Git 记得的一个项目」,本课其余的一切都是你在某个 repo 里做的事。
- 是什么
- 一个被 Git 盯着的项目文件夹:文件 + 全部历史。
- 怎么运作
- 历史存在隐藏的
.git文件夹里;工作文件就是你现在看到的样子。 - 例子
git init把一个文件夹变成 repo;git clone把别人的 repo 整个复制下来,历史也在内。- 注意
- 删掉
.git,只剩文件——整个历史就没了。 - 关联
- 由 Commits 组成;分为 local(你的)和 remote(在服务器上)。
版本控制用一个项目和一份完整、可恢复的历史,取代了那个塞满 _final_FINAL 副本的文件夹。Git 把这份历史存成一条快照时间线。
Git 里最重要的一个词就是 commit——时间线上一份保存下来的快照。弄懂一次 commit 是怎么产生的,你就弄懂了整个系统的心跳。
Commit — 一份保存下来的快照
你整个项目在选定瞬间的一张照片,订着一张说明那一刻是什么的便条——永久钉在时间线上。
commit 是项目历史中的一份快照:文件的一个被记录下来的状态,带着唯一的 ID(像 e81f53 这样的 hash)和一条说明。每个 commit 都指回它前面那个——它的 parent——形成一条链,这条链就是你的历史。你不会不停地 commit,而是在有意义的节点——「加了登录」「修了日期 bug」——这样时间线读起来就是一连串有意为之的步骤,每一步都是你能回去的地方。
- 是什么
- 项目的一份保存下来的快照,带有 ID、作者、时间和 message。
- 怎么运作
- 每个 commit 指向它的 parent;parent 链就是历史。
- 例子
git commit -m "fix date bug"创建 commite81f53。- 注意
- commit 只保存你放进 staging area 的内容,而不是你做过的每一处修改。
- 关联
在 Staging Area 里组装,由 Commit Message 标注,最新的一个由 HEAD 标记。
Staging Area — 封箱之前先把箱子装好
桌上的一个快递箱:你把这次要寄的东西恰好放进去,其他的留在一边,然后才封上胶带寄出。
Git 不会自动给你改过的所有东西都拍快照。你要先挑出哪些改动进入下一个 commit,方法是把它们 stage(git add)——放进 staging area,也就是你正在装的那只箱子。然后 git commit 把恰好那只箱子封起来。这两步——先把该放一起的 stage 好,再 commit——正是 Git 历史能保持干净的原因:每份快照里装什么由你决定,而不是把所有改动一股脑倒进去。
- 是什么
- 位于你的修改和 commit 之间的一只「箱子」。
- 怎么运作
git add把改动放进箱子,git commit把它封成一份快照。- 例子
改了
login.js、login.css和notes.txt——只 stage 前两个,commit「add login」。- 注意
- 忘了
git add,这个改动就不会进入 commit。 - 关联
- 从你的工作文件装入,倒进一个 Commit。是小 commit 的基础。
Commit Message — 写给未来的便条
在那只封好的箱子上贴标签,写明里面是什么——这样几个月后有人扫一眼货架,不用拆开就知道每个箱子里有什么。
每个 commit 都带着一条 commit message,说明改了什么、为什么改。它感觉像杂活,但日后是纯粹的黄金:当你在找搞坏了东西的那次改动,或想弄懂某一行为什么存在时,这些 message 就是项目的故事。一条好 message(「fix timezone bug in invoice export」)能把历史从一堵晦涩 ID 组成的墙,变成一份可读的日志。写给六个月后读它的人——通常就是你自己。
- 是什么
- commit 上的一条简短说明:改了什么、为什么改。
- 怎么运作
- 用
git commit -m "…"写下;之后在 log 里显示在每份快照旁边。 - 例子
- 差:「fix」「wip」。好:「fix timezone bug in invoice export」。
- 注意
- 现在省下五秒,六个月后要花上几个小时去挖。
- 关联
- 给 Commit 贴标签;让 Log 变得可读。
HEAD — 你的「你在这里」标记
时间线地图上的「你在这里」圆点——标出你当前站在哪一份快照上。
HEAD 是 Git 指向你此刻在历史中所处位置的指针——通常是当前 branch 上最新的 commit。你做一个新 commit 时,它就加在 HEAD 之后,然后 HEAD 前移到它上面。你很少直接去碰 HEAD,但这个词到处都会出现,知道它只是表示「我在时间线上的当前位置」,你读到的大部分内容就不再神秘了。
- 是什么
- 指向你所在 commit 的指针(通常通过 branch)。
- 怎么运作
- 新 commit 紧接在 HEAD 之后,HEAD 移到它上面。
- 例子
HEAD → main → c4:你在 main 上,位于 commit c4。commit 一下——现在你就在新的那个上了。- 注意
- 这个词在 Git 的提示里看着吓人,其实只是「当前位置」。
- 关联
- 随每个 Commit 移动;Switch 把它移到另一个 branch。
commit 是一份保存下来的快照。你 stage 恰好该放一起的改动,用一条 message 把它们封好——而 HEAD 标出你站在哪里。
branch 是 Git 不再只是一个花哨的保存按钮、而成为让许多人——或许多想法——同时推进又互不踩脚的方式的地方。
Branch — 一条并行的工作线
把手稿复印一份,你可以在自己的那份上大胆涂改重写,而原稿保持干净,别人还能继续在上面工作。
branch 是一条独立的开发线:你自己的那份时间线副本,你可以在上面 commit,而不影响任何人的工作。你为开发一个功能或尝试一个想法创建 branch,在上面随意 commit,主版本在你准备好之前保持原样。branch 让一个团队能在同一个 repo 里同时做十件事而不乱:每个人在自己的线上。
- 是什么
- 一个 repo 内部一条独立的 commit 线。
- 怎么运作
branch 只是贴在某条线最新 commit 上的一个可移动标签;新 commit 会让它前移。
- 例子
git switch -c feature/login——为登录表单创建一个 branch 并切换过去。- 注意
- 一个活得太久的 branch 会偏离 main,合并时很痛苦。
- 关联
从 main 分出,通过 Merge 或 Rebase 回来,在 Pull Request 里被讨论。
main — 被信任的那条线
一本大家都信任的书的官方上架版——草稿在别处写,只有完成并获批的作品才会装订进去。
按惯例,主 branch 叫 main(老一些的 repo 可能叫 master)。它保存项目稳定、可用的版本——你会拿去发布的那个。新工作在其他 branch 上进行,只有在准备好并经过评审后才并入 main。把 main 当作被信任的那条线,而绝不是草稿纸,是大多数健康团队工作流背后那条简单的纪律。
- 是什么
- 主 branch,保存可发布的、可用的版本。
- 怎么运作
- 没人直接往里写;工作在评审后通过 merge 从其他 branch 进来。
- 例子
feature/search和fix/date分支在准备好后合并进 main。- 注意
- 直接 commit 到 main,有可能搞坏大家都依赖的东西。
- 关联
通过 Merge 和 Pull Request 接收工作;每个 Branch 都从它出发。
Switch (checkout) — 切换 branch 会改变你的文件
换一份摊在桌上的草稿——还是同一张桌子,但瞬间显示出完全不同的一个版本的工作。
当你 switch 到某个 branch(git switch,以前是 git checkout),Git 会把你文件夹里的文件改成与该 branch 的快照一致。切到你的功能 branch,你的修改就出现了;切回 main,它们就从视野里消失(被安全保存,没有丢)。Git 里的 branch 又便宜又即时,所以每个功能或修复建一个是很正常的:不是什么沉重的动作,而是日常的一步。
- 是什么
- 把你移到另一个 branch 上的命令。
- 怎么运作
Git 把你文件夹里的文件改写成那个 branch 的快照,并把 HEAD 移到那里。
- 例子
git switch feature/login——login.js出现;git switch main——它被隐藏,但很安全。- 注意
- 未 commit 的修改可能会阻止切换——先把它们 commit 掉。
- 关联
- 在各个 Branch 之间移动 HEAD。
branch 是一条并行的工作线,你可以随意 commit 而不打扰任何人。main 是被信任的版本;新工作待在自己的 branch 上,直到准备好。
分到各个 branch 上的工作,迟早要重新汇合。merge 是 Git 把各条工作线重新合在一起的方式,而 conflict 是它唯一需要你来做决定的时刻。
Merge — 把两个 branch 重新合在一起
把你那份单独草稿上的修改折回官方手稿,于是这本书现在既含原文,也含你的新章节。
merge 把一个 branch 的 commit 合并进另一个——通常是把完成的功能 branch 折回 main。Git 查看两边各改了什么,把它们编织成一条同时包含两份工作的时间线,常常通过一个 merge commit——一个有两个 parent 的 commit。大多数时候这是自动且毫不费力的:两个 branch 改的是不同的文件或同一文件的不同部分,Git 只是把它们缝在一起。
- 是什么
- 把一个 branch 的工作合并进另一个。
- 怎么运作
- Git 比较两边的改动,创建一个带两个 parent 的 merge commit。
- 例子
git switch main→git merge feature/login——登录表单现在进了 main。- 注意
- 如果两边改了同一行,就会出现 conflict,由你来决定。
- 关联
另一种做法是 Rebase。可能引发 Merge Conflict。在团队里它要经过 Pull Request。
Merge Conflict — 对同一行的两次修改
两个编辑各自把同一句话改成了不同的样子——没有机器能知道你想要哪个,所以它停下来问你。
当两个 branch 以不相容的方式改了同一个文件的同一处,就会发生 merge conflict。Git 猜不出哪个版本才对,所以它暂停,标出那个位置,并把两个版本并排给你看。这不是错误,也不是灾难——这是 Git 正确地拒绝悄悄丢掉某人的工作。conflict 第一次碰到时让人害怕,恰恰因为利害(别丢了工作)很清楚。
- 是什么
- 两个 branch 对文件中的同一处改成了不同的内容。
- 怎么运作
- Git 中止这次 merge,并在文件里标出有争议的行。
- 例子
greeting.txt的第 3 行:“Hello, team!” 对 “Hi, world!”。- 注意
- 这不是坏了——这是 Git 拒绝悄悄丢掉某人的工作。
- 关联
出现在 Merge 或 Rebase 期间;由 Conflict Resolution 解决;有短命 branch 时更少见。
Conflict Resolution — 解决 conflict 只是做个选择
读完两句被改写的话,然后写出你真正想要的最终版本——留下一个、另一个,或者两者混合——然后继续往前走。
你通过编辑被标记的文件来解决 conflict,改成你想要的版本——留下你的、留下他们的,或把两者合并——删掉 <<<<<<<、=======、>>>>>>> 这些标记,然后 commit 结果。这就是全部的秘密:conflict 是 Git 把一个只有人能做的决定交给你,而解决就是做出这个决定。当 branch 短命、你又常常 merge 时,conflict 会变得更小也更少,因为两边永远不会漂得太远。
- 是什么
- 手动选出有争议处的最终版本。
- 怎么运作
编辑文件,删掉
<<<<<<</=======/>>>>>>>标记,然后git add并git commit。- 例子
- “Hello, team!” 和 “Hi, world!” 变成了 “Hi, team!”。
- 注意
- 要选得好,就得理解两个版本——不确定就问作者。
- 关联
- 在 Merge Conflict 之后完成 Merge。
merge 把两个 branch 编织成一条时间线。conflict 只是对同一行的两次修改——Git 拒绝去猜,请你来选。
到目前为止,一切都在你的机器上。要和别人合作——或备份你的工作——就意味着把你本地的 repo 连到云端的共享 repo。
Local 与 Remote — 两份副本,origin 是共享的那份
你在家里的个人工作版手稿,以及办公室里整个团队保持同步的共享主稿。
Git 是分布式的:你机器上有一份完整的 repo 副本(local),通常在服务器上还有一份共享副本(remote)——在 GitHub 或 GitLab 这类服务上。你所有的工作都在本地进行——很快,甚至可以离线——然后与 remote 同步,把它分享出去并拉入别人的工作。默认的 remote 按惯例叫 origin。两份副本,刻意保持同步。
- 是什么
local 是你自己那份完整的 repo 副本;remote 是服务器上的共享副本。主要的 remote 叫 origin。
- 怎么运作
- 在本地工作(甚至离线),然后与 remote 同步。
- 例子
- GitHub 上的 repo 是 origin;你和队友各自在笔记本上有一份副本。
- 注意
- 你没有 push 的工作只存在你的笔记本上——笔记本坏了它也就没了。
- 关联
副本通过 Clone / Push / Pull / Fetch 同步;Pull Request 存在于 remote 上。
Clone、Push、Pull、Fetch — 四个同步动作
把办公室的主稿复印带回家(clone),把你完成的页面送回去(push),再收取其他人的新页面(pull)。
四个词在 local 和 remote 之间搬运工作。Clone 做出一份 remote repo 的第一个本地副本。Push 把你的 commit 送上 remote,让别人能看到。Pull 把别人的新 commit 拉进你的副本并合并。Fetch 下载新内容但暂不合并,让你先看看。掌握这四个,你就能协作:它们就是保持同步的全部词汇。
- Clone
git clone <url>——你第一份完整的 remote repo 本地副本。- Push
git push——把你的 commit 送到 remote。- Pull
git pull——下载别人的 commit 并合并进来(fetch + merge)。- Fetch
git fetch——下载新内容但暂不合并:先看看。- 注意
不先 pull 就 push 常会被拒绝:先把别人的改动拉进来,再送出你的。
- 关联
在 Local 与 Remote 之间搬运 Commits;pull 内部会做一次 Merge。
Pull Request — 团队如何评审工作
把你的新章节交给编辑,他们阅读、评论、批准之后,才会装订进官方的书里。
在 GitHub 这类平台上,你通常不会直接 merge 进 main。你 push 自己的 branch,然后开一个 pull request(PR;在 GitLab 上叫 merge request)——一份合并你工作的提议,队友会在其中评审、留评论,并在它并入 main 之前批准。这是第二双眼睛发现问题、团队确认某个改动已准备好的检查站。团队里的 Git 大多数就发生在 PR 里。
- 是什么
- 把一个 branch 合并进 main 的提议,附带讨论和评审。
- 怎么运作
- push branch → 开 PR → 评论与修改 → 批准 → merge。
- 例子
- PR「add login form」:队友请你给一个函数改名,你 push 了修改,PR 获批。
- 注意
- 没人会真去读一个巨大的 PR——保持它们小。
- 关联
存在于 Remote 上,承载一个 Branch,以 Merge 进 main 收尾。
local 是你的副本;remote(origin)是共享的那份。clone、push、pull 和 fetch 让它们保持同步——而 pull request 是团队在合并前评审的地方。
Git 最深的安慰在于:几乎没有什么是真正丢掉的。知道如何读 history、如何安全地退回去,会让 Git 从吓人变成让人安心。
Log — 项目的故事
翻阅一叠带日期、有署名的每一版草稿——准确看到每一步改了什么、是谁改的。
log(git log)是 commit 的列表,按顺序排列的你项目的历史,每条都带着它的 message、作者和时间。它回答「发生了什么,什么时候?」,是弄懂任何代码库或追查任何 bug 的起点。出问题时,log 让你找到引入问题的那个确切的 commit,因为每个改动都被记录了。一份你能读懂的历史,就是一份你能调试的历史。
- 是什么
- 每个 commit 的列表:ID、作者、时间、message。
- 怎么运作
git log从最新的 commit 到最旧的依次显示历史。- 例子
- 日期在 6 月 11 日坏了——log 显示 commit
e81f53「change date parsing」。 - 注意
- log 有多大用处,取决于里面的 commit message 写得怎么样。
- 关联
- 读取 Commits 组成的链;它的价值来自 Commit Message。
Revert 与 Reset — 两种撤销:诚实的和改写的
撤销一个错误的两种方式:在记录里加一条标注清楚的更正(revert),或者悄悄撕掉最后一页,仿佛什么都没发生过(reset)。
Git 给了你安全退回去的办法。Revert 创建一个新 commit 来撤销之前的某个 commit——错误留在历史里,它的撤销记录叠在上面,这既诚实,对共享的工作也安全。Reset 把你移回到较早的一个 commit,改写近期历史,就好像那些 commit 从没发生过——强大,但对别人已经拿到的工作很危险。经验法则:公开的东西用 revert,只有私人的、本地的工作才用 reset。
- Revert
git revert C——一个撤销 C 的新 commit。历史保持完整。- Reset
git reset B——把 branch 移回 B,就像 C 从没发生过。- 例子
在 main 上用一个 commit 搞坏了生产环境——revert。在本地搞砸了、从没 push 过——reset。
- 注意
- 对队友已经拉走的内容做 reset,会让整个团队失去同步。
- 关联
- 作用于 Commits 和 HEAD;reset 和 Rebase 一样会改写历史。
Rebase — 重放你的工作;遵守黄金法则
不是把你那份旁支草稿带着明显接缝订到手稿上(merge),而是把你的修改干净地重写在最新版本之上,就像你一开始就是从它出发的(rebase)。
Rebase 是 merge 的替代做法:它不是用一个 merge commit 把两条线连起来,而是把你的 commit 重放到另一个 branch 之上,得到一条整洁的直线历史。在分享之前整理你自己的 branch,它非常好用。但它会改写 commit(会创建它们的新副本),所以有一条黄金法则:永远不要对别人已经拉走的历史做 rebase。改写共享的历史,会让所有人失去同步。私人的工作用 rebase;合并共享的工作用 merge。
- 是什么
- 把你的 commit 重放到另一个 branch 之上。
- 怎么运作
- Git 取出你的 commit,在新的基点上重新创建它们的副本。
- 例子
在
feature上运行git rebase main——你的 X 和 Y 现在落在 main 上新的 C 之后。- 注意
- 黄金法则:永远不要对别人已经拉走的历史做 rebase。
- 关联
Merge 的替代做法;可能引发 Merge Conflict;和 Reset 一样会改写历史。
log 是可读的故事;revert 和 reset 是你退回去的办法。rebase 能得到整洁的历史——但永远不要改写别人已经拉走的历史。
Git 有很多命令,但好的实践只是一份简短的习惯清单。照着做,Git 就始终是一张让人安心的安全网,而不是偶尔发生的危机。
Small Commits — 小而专注的 commit
一本相册,每张照片捕捉一个清晰的瞬间并配着说明——比一张把所有东西一次拍糊的照片有用得多。
小步且频繁地 commit(小的、原子的 commit),让每个 commit 都是一个连贯的改动,并带一条解释它的 message。细小又标注清楚的 commit,让历史可读,bug 易于追踪,撤销也精准:你可以 revert 一个精确的改动,而不丢掉其他任何东西。一个把一周混杂的工作一股脑倒进去的巨型 commit 则正相反——无法评审,无法理清,也无法部分撤销。这条纪律只花几秒,却在项目的整个生命周期里持续回报。
- 问题
- 一个巨型 commit 无法评审、无法理清,也无法部分撤销。
- 思路
- 一个 commit = 一个连贯的改动 + 一条清晰的 message。
- 例子
不要「week of work」——而是「add login form」「style login page」「fix date bug」。
- 代价
- 花几秒钟去
git add对的文件并写好 message。 - 关联
建立在 Staging Area 和 Commit Message 之上;让 Revert 和 Log 变得有用。
Feature Branch Workflow — 每个功能一个 branch,常常合并
每个新章节在自己的副本上起草,趁它还小就折回去——这样没有哪份草稿会偏离全书太远,以至于重新合并变成一场硬仗。
每一项工作都在它自己的 branch(功能 branch)上做,保持它短命,并常常合并回去。长命的 branch 会偏离 main 很远,让 merge 变成一堆痛苦的 conflict;小 branch 频繁合并则始终轻松。再配上先 pull 再 push——先把别人的改动拉进来——你就是在持续集成,而不是到最后才撞车。这个节奏是顺畅的团队 Git 的核心。
- 问题
- 所有人都往 main 写——互相搞坏;一条长 branch——最后一座 conflict 的大山。
- 思路
- 一个任务——一个短 branch,通过 PR 在几天而不是几周内合并。
- 例子
fix/date:pull、建 branch、两个 commit、push、PR、评审、merge——一天内搞定。- 代价
- 你得把工作切成小块,并且常常 pull main。
- 关联
把 Branch、Pull、Push、Pull Request 和 Merge 串在一起。
这些概念如何连在一起
一条流水线:从文件里的一处修改到被信任的版本——一条路,沿途有清晰的站点。
整条路是这样的。你的工作文件里的修改,通过 git add 进入 staging area,再通过 git commit 变成一个 commit,你在 log 里读到它。commit 在 branch 上累积;branch 通过 merge 或 rebase 回来,有时会遇到 conflict。在团队里,你把 branch push 到 remote,开一个 pull request,评审之后 merge 进 main。如果出了问题——revert 或 reset。
- 开工前先拉取最新,这样你是在当前的工作之上构建。
- 在 branch 上工作,绝不直接在脆弱的 main 上。
- 边做边写小 commit 和清晰的 message。
- 常常 merge,趁 branch 还短,让 conflict 保持很小。
- Push 你的工作,并开一个 pull request 请人评审。
- 共享的工作用 revert 撤销;永远不要对别人已有的历史做 rebase。
- 你的历史读起来像一个由带标签的小步骤组成的清晰故事。
- 新工作在 branch 上;main 始终能用。
- 你先 pull 再 push,并趁 branch 还小就 merge。
- 共享的工作你用 revert,绝不改写共享的 history。
- Git 给你的感觉像一张安全网——你放心地实验,因为知道总能回得来。
好的 Git 就是几个习惯:先 pull、把工作放在 branch 上、写小 commit 和清晰的 message、常常 merge、merge 前先评审。命令很多;纪律很短。
速查表:全部 21 个概念
基础
- Version ControlVCS
一个项目 + 完整历史;任何版本都能回来。
- Git快照时间线
最流行的版本控制系统;把项目存成快照。
- Repositoryrepo
项目文件夹 + 藏在
.git里的历史。
commit
- Commit快照
一份保存下来的快照,带 ID 和指向其 parent 的链接。
- Staging Area暂存区
commit 之前的箱子:
git add装箱,git commit封箱。- Commit Message便条
写给未来的便条:改了什么、为什么改。
- HEAD你在这里
指向当前 commit 的指针;随每个 commit 前移。
branch
- Branch工作线
一条不碰别人工作的并行 commit 线。
- main主分支(master)
被信任的、始终可用的版本。
- Switch / Checkout切换分支
切到一个 branch:文件夹里的文件变成它的快照。
merge 与 conflict
- Merge合并
把 branch 合到一起;常通过一个有两个 parent 的 commit。
- Merge Conflict冲突
两个 branch 把同一行改成了不同内容;Git 请你来决定。
- Conflict Resolution解决冲突
选出最终版本,删掉标记,commit。
remote 与团队
- Local / Remoteorigin
你那份完整的副本和服务器上的共享副本。
- Clone / Push / Pull / Fetch同步
第一份副本 · 送上你的 · 拉下他们的并合并 · 只下载。
- Pull RequestPR,评审
评审后把 branch 合并进 main 的提议。
撤销与 history
- Log历史
commit 的列表:谁、何时、做了什么、为什么。
- Revert vs Reset撤销
Revert——一个新的撤销 commit(用于共享的工作)。Reset——改写(只用于你自己的)。
- Rebase重放
把 commit 重放到另一个 branch 之上。绝不用于共享的历史。
好习惯
- Small Commits原子 commit
一个改动——一个带清晰 message 的 commit。
- Feature Branch Workflow每个功能一个 branch
每个任务一个短 branch,通过 PR 常常合并。