【Git】Git 骚操作:rebase
, cherry-pick
和 reflog
让你成为团队高手
所属专栏: 《前端小技巧集合:让你的代码更优雅高效》
上一篇: 【工程化】包管理也内卷?pnpm, npm, yarn 的小技巧与最佳实践
作者: 码力无边
✨ 引言:你的 Git 历史,是一本“天书”,还是一部“史诗”?
嘿,各位在代码时间线中穿梭的道友们,我是码力无边!
在我们的前端开发江湖中,Git 就像是我们每个人都必须掌握的“时空穿梭机”。git add
, git commit
, git push
, git pull
这“四件套”,是我们每天都在念诵的“基础咒语”,它们能带我们完成最基本的代码同步和版本记录。
但是,当团队协作变得复杂,当项目的历史开始交错,你的“时空之旅”是否也曾陷入混乱?
- 你的
git log
是不是充满了各种Merge branch 'develop' into feature/xxx
这样的“噪音”,让代码的演进历史变得难以追溯? - 你是否曾想过,只把另一个分支上的某一个“神之一手”的提交,精准地“移植”到当前分支,却只能无奈地手动复制粘贴代码?
- 你是否曾因为一次错误的
reset --hard
或rebase
操作,眼睁睁看着自己辛辛苦苦写的代码消失在“时间的虚空”中,然后抱着头不知所措?
如果这些场景让你感同身受,那说明你的“时空穿梭机”还停留在“手动挡”模式。是时候升级你的驾驶技巧,学习几招能让你“操控时间线”的“骚操作”了。
今天,码力无边就要传授你三招 Git 的上乘心法,它们分别是:
git rebase
: 变基——让你的提交历史变得像一条直线一样清晰的“整容术”。git cherry-pick
: 摘樱桃——精准“采摘”任意提交的“隔空取物”神技。git reflog
: 引用日志——你的 Git “后悔药”,能带你找回几乎所有“丢失”的提交。
掌握它们,你将从一个只会“保存进度”的 Git 使用者,蜕变成一个能够精心雕琢提交历史、从容应对各种复杂协作场景的 Git 高手。你的 Git 历史,将不再是一本无人能懂的“天书”,而是一部清晰、优雅、可歌可泣的“开发史诗”。
一、git rebase
:让你的提交历史“整洁如新”
rebase
(变基)和 merge
是合并分支的两种不同方式。它们的目的相同,但实现的过程和最终形成的历史记录却截然不同。
git merge
:忠实的历史记录员
merge
会创建一个新的“合并提交”(merge commit),它有两个父节点,分别指向被合并的两个分支的末端。它忠实地记录了“在某个时间点,我们把 B 分支合入了 A 分支”这个事实。
优点:保留了最真实、最完整的历史信息。
缺点:当团队频繁地合并功能分支时,提交历史会变得像一张复杂的蜘蛛网,充满了分叉和合并节点,难以阅读。
git rebase
:优雅的历史小说家
rebase
则会“重写历史”。它的核心思想是:找到两个分支的共同祖先,然后把你当前分支上独有的提交,像“嫁接”一样,一个个地“重新播放”到目标分支的最新提交之后。
场景: 你从 develop
分支切出了一个 feature/login
分支进行开发。在你开发期间,develop
分支上又有了新的提交。现在你想把 develop
的最新进展同步到你的功能分支。
使用 merge
的操作:
git checkout feature/login
git merge develop # 会产生一个合并提交 "Merge branch 'develop' into feature/login"
历史线图会多一个分叉和合并点。
使用 rebase
的操作:
git checkout feature/login
git rebase develop # 见证奇迹的时刻
发生了什么?
rebase
会:
- 暂时“收起”你在
feature/login
上的所有提交(比如 C4, C5)。 - 将
feature/login
分支的指针移动到develop
分支的最新提交(C3)上。 - 然后,把你刚才“收起”的提交(C4, C5),以全新的提交 ID(C4’, C5’),依次应用在新的基点(C3)之后。
最终,你的 feature/login
分支看起来就像是直接从最新的 develop
分支上拉出来的一样,整个提交历史变成了一条干净的直线!
交互式变基:git rebase -i
rebase
最强大的地方在于它的交互模式 rebase -i
(interactive)。它能让你在“重新播放”提交之前,对这些提交进行精细的“编辑”。
# 假设你想整理你最近的 3 个提交
git rebase -i HEAD~3
执行后,Git 会打开一个编辑器,列出这 3 个提交,每个提交前都有一个 pick
关键字。你可以修改这个关键字来执行不同的操作:
pick
: 保留该提交(默认)。reword
(或r
): 保留该提交,但修改提交信息。edit
(或e
): 保留该提交,但rebase
会在该提交处暂停,让你有机会修改代码内容。squash
(或s
): 将该提交合并到它的前一个提交中,并让你重新编辑合并后的提交信息。fixup
(或f
): 同squash
,但直接丢弃该提交的提交信息。drop
(或d
): 直接删除该提交。
你还可以拖动行的顺序来调整提交的顺序!
黄金法则:永远不要在已经推送到远程的公共分支(如 main
, develop
)上执行 rebase
!
因为 rebase
会重写历史,改变提交 ID。如果你在一个多人协作的分支上这么做,会给其他已经拉取了旧历史的团队成员造成巨大的混乱。rebase
主要用于整理你自己的、还未推送或只影响你个人的功能分支。
二、git cherry-pick
:精准的“外科手术”
cherry-pick
(摘樱桃) 是一个非常有用的命令,它允许你选择任意分支上的一个或多个提交,并将它们作为新的提交,“复制”到你当前所在的分支上。
场景:
- Bug 修复:你在
feature/A
分支上修复了一个紧急的 bug(提交commit-A
),但这个 bug 同时也存在于release
分支上。你不想将整个feature/A
分支合并到release
,你只需要那个修复 bug 的commit-A
。 - 功能复用:你在
feature/B
分支上写了一个非常通用的工具函数(提交commit-B
),现在feature/C
分支也需要这个函数,但 B 和 C 的功能完全不相关。
操作方法:
- 首先,找到你想要“采摘”的那个提交的 SHA-1 哈希值(可以通过
git log
查看)。# 在 feature/A 分支上 git log # commit a1b2c3d4... (这是修复 bug 的提交) # Author: 码力无边 # Date: ... # # fix: 修复了一个严重的 bug
- 然后,切换到你的目标分支,执行
cherry-pick
。
完成!git checkout release git cherry-pick a1b2c3d4
commit-A
的所有代码变更,现在已经被应用到了release
分支上,并生成了一个新的提交(拥有新的 SHA-1 值)。
你也可以同时“采摘”多个提交:
git cherry-pick commit-A commit-B commit-C
或者一个连续的提交范围:
# 采摘从 commit-A (不含) 到 commit-C (含) 的所有提交
git cherry-pick commit-A..commit-C
cherry-pick
就像一个精准的外科医生,能让你在复杂的分支网络中,精确地提取和复用代码变更,而无需进行笨重的分支合并。
三、git reflog
:你的“时光倒流”护身符
reflog
(Reference Log,引用日志) 是 Git 的一个“隐藏”机制,但它却是你最重要的安全网。Git 会在本地仓库中,默默地记录下你的 HEAD
指针和分支指针每一次移动的历史。
git log
记录的是项目的提交历史,它是项目的一部分,会被推送到远程。而 git reflog
记录的是你本地的操作历史,它只存在于你自己的电脑上。
场景: 你搞砸了一次 rebase
,或者不小心执行了 git reset --hard HEAD~5
,删掉了最近 5 个还未推送的提交。你以为你的代码永远消失了,内心一片恐慌。
reflog
来拯救你!
在你的终端里,立刻输入:
git reflog
你会看到一个类似这样的列表:
a1b2c3d HEAD@{0}: reset: moving to HEAD~5
f9e8d7c HEAD@{1}: rebase (finish): returning to refs/heads/my-feature
f9e8d7c HEAD@{2}: rebase (squash): Squashed commit
...
e4d5f6g HEAD@{5}: commit: 添加了新功能
...
这个列表,就是你的“时空穿梭机”的操作记录!它记录了你每一次 commit
, rebase
, reset
, merge
, checkout
等操作。
每一行最左边的 a1b2c3d
就是那次操作之后 HEAD
指向的提交 ID。HEAD@{n}
表示 HEAD
在 n
次操作前的位置。
如何恢复?
- 在
reflog
中找到你误操作之前的那个状态。比如,e4d5f6g HEAD@{5}
是你执行reset
之前的最后一个正确提交。 - 使用
git reset
强制将你的分支指针移回到那个“正确的时空坐标”:
或者,如果你想更保险一点,可以先创建一个新分支来查看:# 强制将当前分支恢复到 e4d5f6g 这个提交的状态 git reset --hard e4d5f6g
git checkout -b recovered-branch e4d5f6g
就是这么简单!reflog
就像是 Git 的“黑匣子”,为你保留了最后的希望。只要提交曾经在你的本地仓库中存在过,reflog
大概率就能帮你找回来(默认会保留 90 天)。
写在最后:工具塑造思维,思维升华工具
rebase
, cherry-pick
, reflog
,这些命令初看起来可能比 merge
和 commit
要复杂。但它们所代表的,是一种更主动、更精细地管理代码历史的思维方式。
rebase
教会我们追求清晰的叙事线,让代码历史服务于未来的读者。cherry-pick
教会我们模块化地思考变更,将每一个提交都视为一个可独立应用的“补丁”。reflog
则给了我们敢于尝试的底气,让我们在探索高级功能时,总有一条安全的退路。
熟练掌握这些“骚操作”,并理解它们背后的设计哲学,将让你在团队协作中游刃有余。你不仅能提交功能,更能贡献出一段清晰、健壮、易于维护的“开发史诗”。而这,正是一位优秀工程师的核心价值所在。
专栏预告与互动:
我们已经掌握了 Git 的高级技巧。接下来,让我们回归到代码本身。在现代前端开发中,TypeScript 已经成为了事实上的标准。它强大的类型系统能帮助我们构建更大型、更可靠的应用。
下一篇,我们将深入 TypeScript 的世界,从最实用的工具类型(Utility Types)开始,看看如何用
Partial
,Pick
,Omit
等工具,轻松地对现有类型进行“变形”,实现灵活的类型编程!感觉码力无边的 Git“骚操作”让你大开眼界?别忘了点赞、收藏、关注,你的每一次支持,都是我挖掘更多“独门秘籍”的强大动力!
今日论道: 在你的团队中,是更推崇使用
git merge
还是git rebase
来合并功能分支?这两种策略分别对团队的工作流和代码审查(Code Review)有什么不同的影响?在评论区分享你团队的实践和见解,我们一起探讨!