1、分支的概念和使用
1.1、什么是分支?
分支(Branch)是在版本控制中非常重要的概念。几乎所有版本控制系统都支持某种形式的分支。在 Git 中,分支是 Git 强大功能之一,它允许我们从主开发线分离出来,在不影响主分支的情况下并行地进行开发 (Git 分支管理 | 菜鸟教程)。你可以把分支想象成代码开发的平行宇宙:在一个分支上进行的修改,不会立即出现在另一个分支上。开发者可以利用分支来尝试新功能、修复 Bug 或进行实验,而无需担心弄乱主分支(通常是用于发布的稳定分支)。完成工作后,再把分支的改动合并回主分支,这样主分支就得到了更新,而整个过程主分支始终保持可用和稳定。
简而言之,使用分支可以让团队并行开发成为可能:每个人或每个新功能都在自己的分支上进行,互不干扰。等到需要整合时,再将分支合并。下面我们将介绍 Git 中有关分支操作的常用命令:创建分支、切换分支以及合并分支。
1.2、创建和切换分支(git branch, git checkout)
在 Git 中,新建分支使用命令 git branch
,切换分支使用命令 git checkout
(在较新的 Git 版本中,也可以使用 git switch
切换分支)。我们先来看如何创建一个分支:
-
git branch <分支名>
:创建一个新的分支。此命令将在当前所在提交的基础上创建出一个分支指针。注意,仅执行git branch
命令不会切换到新分支,只是新建了一个分支。
例如,如果我们当前在主分支 main
上,希望创建一个新分支来开发登陆功能,可以执行:
git branch login-feature
此操作会在当前提交的基础上建立一个名为 login-feature
的新分支。但是此时我们仍停留在 main
分支上。通过 git branch
不带参数可以列出所有本地分支,*
会标识当前所在的分支。
-
git checkout <分支名>
:切换到指定的分支。切换分支会更改工作区的文件内容,以匹配该分支最近一次提交的状态。执行切换前,确保当前工作区没有未提交的修改,否则 Git 会拒绝切换(或者要求先 stash 暂存修改)以防止未提交的更改被覆盖。
继续上面的例子,我们创建了 login-feature
分支后,可以切换过去:
git checkout login-feature
执行后,Git 会提示已经切换到 login-feature
分支。现在开始的所有提交都会在 login-feature
分支上,而对 main
分支没有影响。
一个常用的快捷方式是将创建和切换合为一步完成:git checkout -b <分支名>
。例如:
git checkout -b login-feature
如果 login-feature
不存在,这条命令会创建该分支并马上切换过去,相当于依次执行了 git branch
和 git checkout
。这样更方便一些。
成功切换到新分支后,你可以像往常一样编辑代码、添加和提交。所有这些提交将属于 login-feature
分支,与主分支main
的提交历史分离开。你可以随时使用 git checkout main
切换回主分支,如果此时打开项目文件,会发现那些在 login-feature
分支上做的改动不见了(因为此刻看到的是主分支上的内容)。别担心,这些改动仍然安全地保存在 login-feature
分支,只是当前未合并到主分支而已。
小贴士:在多人协作中,通常每个功能或修复都会新建一个分支进行开发,分支命名应简洁明了,如
feature/login-ui
或bugfix/issue-101
等。主分支一般用来保存已经稳定并准备发布的代码。
1.3、合并分支(git merge)
当一个分支上的开发告一段落,需要将其成果合并回主分支(或其他分支)时,就可以使用 git merge
命令。git merge
用于将另一条分支的修改合并到当前分支上。
最常见的场景是将功能分支合并回主分支。例如,我们在 login-feature
分支上完成了登陆功能的开发和测试,现在希望把它合并到 main
。步骤如下:
-
首先,切换到目标分支(即我们希望将改动合并到的分支)。这里是主分支
main
:git checkout main
-
然后执行合并命令,将功能分支合并过来:
git merge login-feature
这条命令表示:“把分支
login-feature
合并到当前分支(main)”。
执行 git merge
后,可能出现两种情况:
-
快速前进(Fast-forward)合并: 如果主分支在功能开发期间没有新的提交,那么主分支实际上停留在功能分支的起点上。在这种情况下,Git 不需要创建新的合并提交,而只会简单地将主分支指向功能分支的最新提交。这种合并被称为 “快进”,表现出来就是主分支的历史直接向前推进,包含了功能分支的提交记录。
-
非快进合并: 如果主分支在功能分支开发期间有自己的新提交,那么两个分支的历史出现了分叉。Git 会进行一次“三方合并”(three-way merge),自动创建一个新的合并提交来把分叉的历史合并起来。合并提交包含两个父提交,一个来自主分支原来的末尾,一个来自功能分支的末尾。合并提交的默认说明一般是“Merge branch 'login-feature' into main”。
在大多数情况下,如果没有冲突,Git 会自动完成以上两类合并。合并后,可以使用 git log --graph --oneline
来查看历史,分支的提交记录现在都出现在主分支的历史中了。完成合并后,功能分支的代码已经融合进主分支,通常我们可以选择删除那个已完成的功能分支以保持仓库整洁。删除本地分支的命令是:
git branch -d login-feature
-d
选项表示删除分支(该操作不会影响已经合并到的内容)。如果分支尚未合并就尝试删除,Git 会发出警告并拒绝删除,以免丢失未合并的修改。若确定要强行删除,可以用 -D
(不推荐轻易使用)。
注意:合并前请确保将待合并的分支最新内容拉取下来(如果是多人协作且远程有更新的话)。比如在合并远程分支时,先
git pull origin login-feature
更新本地的login-feature
,再切换到 main 合并。这样可以减少合并冲突的概率。
2、合并冲突及解决方法
当两个分支都有各自的提交,并且修改了同一个文件的同一部分时,Git 在合并它们时就会遇到合并冲突(merge conflict)。简单来说,Git 无法自动判断以哪个分支的修改为准,需要人工介入决策。冲突最常发生于多人修改了相同代码的情形,或自己在不同分支对同一文件做了不同改动。
**冲突发生时的现象:**执行 git merge
后,终端会输出类似“Automatic merge failed; fix conflicts and commit the result.”的信息。这表示自动合并未能完成,需要手动解决冲突。此时,可以运行 git status
查看哪些文件存在冲突,输出中会列出冲突文件并标记为“both modified”。
解决合并冲突的一般步骤:
-
定位冲突区域: 用文本编辑器打开存在冲突的文件。你会看到特殊的冲突标记。例如,假设
config.txt
文件在两个分支有冲突,打开后可能出现这样的内容:<<<<<<< HEAD timeout=30 ======= timeout=60 >>>>>>> login-feature
上述标记表示:
HEAD
(当前分支,即 main 分支)的内容是timeout=30
,而要合并进来的login-feature
分支的内容是timeout=60
。冲突区域被<<<<<<<
、=======
、>>>>>>>
分隔成两部分,开发者需要决定哪一侧(或如何合并两者)才是正确的。 -
手动合并修改: 编辑冲突标记所在位置,根据需要保留或修改代码。例如,如果确定以
login-feature
分支的设置为准,就把timeout=60
保留下来,删去其他冲突标记。如果两侧的修改都需要,可以手动合并成一个新的内容。总之,要编辑成我们想要的最终结果,并删除所有<<<<<<
,======
,>>>>>>
标记,使文件恢复正常的代码格式。 -
标记冲突已解决并暂存: 修改完所有冲突文件后,保存文件并退出编辑器。然后使用
git add <文件>
将刚刚解决冲突的文件添加到暂存区。标记为已解决意味着告诉 Git:“这文件的冲突我已经处理好了,可以纳入下一次提交”。 -
完成合并提交: 当所有冲突都解决并
git add
后,再次执行git status
检查确认没有剩余未解决的冲突。接下来,运行git commit
提交合并结果。值得注意的是,如果当前正处于合并过程,Git 在提交时会自动带上合并的默认提交信息(也可以自行编辑提交说明)。提交成功后,合并冲突就算解决完成,仓库历史中会出现这次合并提交。
如果在处理冲突过程中发现合并的方向有误或者想放弃此次合并,可以使用:
git merge --abort
该命令会中止合并操作,将分支恢复到合并开始前的状态,就像从未执行过合并一样。然后你可以重新尝试合并或者做其他处理。
小提示:合并冲突虽然听起来可怕,但有些图形化工具可以帮助解决。例如,使用
git mergetool
可以调起图形化冲突解决工具(如 KDiff3、P4Merge 等)对比冲突双方的差异,辅助你选择保留方案。另外,在团队协作中,减少冲突的最好办法是勤奋地沟通和同步:经常git pull
同步他人修改,及时分享自己的改动。当冲突真的发生时,也尽量和相关开发者商量确认最终修改方案。
3、使用 git stash 暂存变更
在日常开发中,有时我们会遇到这样一种情况:正在一个分支上开发新功能,代码写到一半,突然接到任务需要立即切换到另一分支去修复紧急问题。但当前分支的修改尚未完成也不适合提交,这时该怎么办?直接切换分支会被 Git 拒绝(因为有未提交修改),把不完整的工作硬提交显然也不妥当。git stash 就是为了解决这种场景而设计的。
git stash
命令可以将当前工作目录和暂存区的修改暂时储藏起来,恢复到干净状态,好让你切换到别的分支去处理其他事务。等需要恢复时,再将暂存的修改取出,继续先前的工作。git stash
类似于一个堆栈(stack),可以储存多组未完成的修改。
常用的 stash 操作为:
-
保存当前修改: 在有未提交改动的情况下运行:
git stash
这会把所有未提交的修改(包含已经
git add
暂存的和尚未暂存的)保存到一个匿名的储藏当中。Git 会输出类似 “Saved working directory and index state WIP on branchName...” 的信息,并将工作区恢复到最近一次提交的状态。你也可以加说明信息,例如git stash save "message"
,但较新的 Git 版本中直接git stash
也可以自动保存。 -
查看暂存列表: 可以运行:
git stash list
来查看所有储藏的记录。最新的储藏在最上面,每条记录都有一个索引(如
stash@{0}
)和描述(包含当时所在的分支名和提交信息的摘要)。 -
恢复暂存的修改: 当你要继续之前的工作时,在相应的分支上运行:
git stash pop
这条命令会将最近一次保存的修改取出应用到当前工作目录,同时从储藏列表中移除该记录。假如你想应用但保留储藏记录,可以使用
git stash apply stash@{n}
(其中n
是编号)来应用某个特定储藏但不删除它。
举个例子:你在 feature-X
分支上开发,写了一半代码。这时需要切换到 main
分支修复紧急 Bug。你可以执行 git stash
将 feature-X
的修改暂存起来,然后 git checkout main
切换分支(现在不会有未提交内容的阻碍)。修复完 Bug 并提交推送后,回到 feature-X
分支,用 git stash pop
将之前的工作恢复出来,继续完成剩下的开发。整个过程就像把工作“按暂停”,处理完其他事情后再“恢复播放”。
git stash
非常适合处理工作中断、上下文切换的场景。需要注意的是,stash 默认会储存未提交的所有内容(包括已暂存和未暂存的部分)。如果你只想暂存尚未暂存的修改而保留已暂存部分,可以使用 git stash -k
(keep index)。另外,git stash
也能暂存新建但未追踪的文件(加 -u
参数),不过初学者一开始用默认行为即可。
最后,当某个储藏的修改不再需要时,可以用 git stash drop stash@{n}
删除,或者 git stash clear
清空所有暂存记录。合理地使用 stash,可以帮助你保持仓库提交历史的整洁,不会因为临时切换任务而到处留下零碎的“WIP”提交。
4、git reset 与 git revert 的区别
在使用 Git 的过程中,难免会遇到需要“撤销”更改的时候。Git 提供了多个命令来撤销或回退提交,其中最常见的是 git reset
和 git revert
。它们都能达到“让项目回到之前状态”的目的,但原理和使用场景有所不同。理解两者区别有助于我们在不同情况下选择正确的方法去撤销修改。
4.1、git reset —— 重置提交历史
git reset
通常用于“回退”本地仓库的HEAD到某个旧的提交。通过 reset,我们可以舍弃最近的一些提交,使仓库退回到指定的历史节点。根据参数不同,git reset
既可以影响提交历史,也可以影响暂存区和工作区。
git reset [模式] <提交>
其中常用的模式有三种:
-
--mixed
(默认模式):重置 HEAD 到指定提交,同时保留工作区改动但清除暂存区。简言之,就是把选定提交之后的修改全部撤出提交历史,放回工作区未暂存的状态。 -
--soft
:更温和的重置,只重置 HEAD 提交,暂存区和工作区都保留这些改动。也就是说撤销最近的提交,但改动仍然保持在暂存区,几乎相当于把提交“取消”回暂存状态。 -
--hard
:强制重置,提交历史、暂存区和工作区都同步到指定提交的状态。所有在那个提交之后的改动都会被彻底丢弃,从当前目录中消失(无法通过 Git 恢复)。
一个常见的用法是撤销最近一次提交。例如你发现刚才的提交有问题,不想要了,可以使用:
git reset HEAD~1
这条命令将仓库重置到上一个提交(即舍弃了最新提交)。默认模式下(mixed),最新提交的更改内容会留在工作区,这样你可以修改后重新提交,或者也可以放弃掉。相当于“退一步,但保留现场改动,允许你修正后再提交”。
如果你完全确定最近的提交是错误的且不需要其中的改动,可以用:
git reset --hard HEAD~1
警告:--hard
非常危险,它会抹掉工作区中自上一个提交以来的所有改动!除非你在其他地方还有这些改动的备份,否则此操作不可逆。因此务必谨慎使用。
git reset
更多地被视为一种本地操作,设计用于调整尚未公开(push)的历史。例如,你提交了一些临时代码想回退,或者想将上一个 commit 拆分/合并,这些都可以通过 reset 在本地实现,然后重新整理 commit,再推送。但是如果你已经把提交推送到远程仓库,其他人也基于它展开了工作,贸然 reset 自己的分支并强制推送(force push)会导致团队其他人的仓库历史不一致。因此,git reset
一般不应用于已发布到公用仓库的提交。
除了回退整个提交,git reset
还可以用于取消暂存(unstage)。比如不小心 git add
了不想提交的文件,可以用 git reset HEAD <文件>
将其从暂存区移除,回到未暂存状态。
4.2、git revert —— 还原提交
与 reset 不同,git revert
是通过创建一个新的提交来抵消某个历史提交的影响,从而达到“撤销更改”的目的。git revert <提交>
会根据指定的提交,产生一个内容相反的新提交,应用这个新提交后,仓库状态看起来就像撤回了指定的提交一样。但是,原先的提交记录依然保留在历史中,只是后面附加了一个“反向提交”来消除它的效果。
git revert
常用于已经推送到远程仓库并分享给他人的提交的撤销。因为 revert 不会重写历史,它是往历史里添加记录,所以对团队其他成员不会产生破坏性的影响。实际上,revert 后大家同步代码时,会拿到一个新的提交,该提交做了撤销更改的操作。
举例来说,假如在主分支有一个错误的提交 C
已经推送,现在想撤销它。如果用 reset,我们必须强制修改历史,会给协作者带来困扰;但使用 revert:
git revert <提交C的哈希>
Git 会自动创建一个新提交 C'
(内容为将提交 C 引入的改动反向修改回去),提交消息会注明它是一次 revert。例如 “Revert "添加XX功能"”。推送这个新提交后,其他人拉取更新,他们的仓库会保留提交 C
和 C'
两条记录,但最终文件内容与 C
未发生前一致了。
git revert
的语法通常是指定具体的提交哈希,也可以使用诸如 HEAD
、HEAD~2
这类引用来表示相对位置。还可以连续 revert 多个提交(需要逐个执行或使用...
范围)。需要注意,如果要 revert 的提交不是最新的,或者涉及文件改动与后续提交有重叠,可能也会出现冲突,处理方式和合并冲突类似:修改冲突文件然后继续 git revert --continue
完成操作(revert 一个提交序列的情况)。
小结比较:git revert
和 git reset
的根本区别在于是否保留历史。git revert
是用一次新的 commit 来回滚之前的 commit,而 git reset
是直接删除指定的 commit 。换句话说:
-
使用
git reset
,仓库会“丢弃”某段历史,就好像那些提交从未发生过。而使用git revert
,仓库历史会完整保留每一次改变,只是附加记录某次改动被撤销了。 -
git reset
改变了当前分支的 commit 链(HEAD 向后移动),而git revert
则让 HEAD 继续前进,只是新增的那个提交的内容与被还原的提交相反,从效果上抵消了它 。 -
从协作角度,reset 要求所有协作者都配合修改历史(否则会出现分歧),而 revert 则是一个正常的提交,其他人只需拉取即可。因此,撤销公开发布的提交建议使用
git revert
;仅在本地调整尚未发布的提交时可以使用git reset
。
使用场景建议:
-
当你提交后立刻发现错误,且该提交尚未推送,想彻底抹除这次错误提交,可以考虑
git reset
将 HEAD 回退(或者用git commit --amend
更正提交,这也是改历史的方法之一)。但如果该提交已经分享出去,不要用 reset 撤销。 -
当某次提交已经推送并被他人拉取,如果后来证明需要撤销,就用
git revert
来安全地回滚。这样所有人的历史都会保持一致,只是多了一条修正记录。 -
如果需要清除多个无用的提交(比如试验性质的提交),而这些提交尚未推送,可以用
git reset
一次性回退。不过Git还有更高级的工具如交互式 rebase 可以用于整理多个提交(见下一节)。
总之,谨记:不要在公共仓库的分支上使用
git reset --hard
等破坏性命令;撤销公开提交请选择git revert
。两者各有用途:reset 修剪历史,revert 保持历史连续性地进行撤销。
5、使用 git rebase 优化提交历史
在多人协作和长期开发的项目中,提交历史很容易变得复杂。频繁的合并会产生很多分支交叉点和合并提交,而且每个人的提交粒度和风格也不尽相同。Git 提供的 git rebase
命令可以在一定程度上优化和整理提交历史,使之更加线性和简洁。不过,rebase 修改历史的特性也需要我们小心使用。
5.1、变基(Rebase)简介
git rebase
的直译是“变基”。它的作用是改变提交的基底。具体来说,就是将一系列提交“剪下来”,然后重新应用(Replay)在另一位置。结果就是提交历史发生了重排或重构。在实际操作中,rebase 常用于两种情况:
-
在分支合并前,更新分支以基于最新的主分支: 假设我们有一个功能分支
feature
从主分支main
分出。一段时间后,main 上有了新的提交,而 feature 也有自己的提交。这时,我们可以在将 feature 合并回 main 之前,先切到 feature 分支执行git rebase main
。这会把 feature 分支上的提交转移到 main 分支最新提交之后,仿佛 feature 是从当前最新的 main 开始开发的一样。这样处理后,当我们再合并 feature 到 main 时,可以避免产生额外的合并提交,使历史呈现为一条直线。例如:最初 main 有 A 提交,feature 分支从 A 分出做了提交 B;期间 main 上又有了提交 C。此时 feature 分支执行
git rebase main
后,feature 上的 B 提交会被移到 C 的后面,形成一个新的提交 B'。现在 main 的历史是 A - C,feature 的历史是 A - C - B'。随后如果 fast-forward 合并,主分支将线性包含 A - C - B',看起来就像 B'是在 C 之后提交的一样,消除了分叉记录。Rebase 后由于提交被重新应用,其 SHA-1 哈希值会发生变化(B 变成 B'),所以这个过程重写了分支的历史。Git 会逐个应用 feature 分支的每个 commit,因此如果这些提交与 main 的更改有冲突,会暂停并要求解决冲突(类似前面合并冲突的过程,但命令变为
git rebase --continue
继续下一个,或者git rebase --abort
放弃变基)。 -
整理本地的多次碎片提交: 在开发一个功能时,可能我们会进行很多次临时的提交(比如调试信息、实验尝试等)。在提交到主分支前,团队往往希望这些历史被整理得整洁一些,例如合并相关的改动、编辑规范提交消息等。
git rebase -i
(交互式变基)可以帮助实现这一点。通过交互式 rebase,我们可以选择**压缩(squash)**多个提交为一个、修改(edit)某些提交的内容或消息、甚至改变提交顺序等。例如,假设我们在当前分支有最近的5个提交需要整理,只需执行:
git rebase -i HEAD~5
Git 会打开一个文本界面,列出这5个提交以及可选的操作(pick, squash, edit 等)。根据需要编辑保存后,Git 就会按照新的指令重新依次应用这5个提交(期间如果有冲突需解决)。整理完成后,分支历史就被改写成了我们想要的样子。常用的操作包括将多次“小提交”合并成一次有意义的大提交(squash),或修改提交信息(reword/edit)以更清晰地描述改动。
rebase vs merge: Rebase 和 Merge 是实现分支集成的两种方式,各有优劣。Merge保留了完整的历史轨迹(包括分叉点和合并节点),能看出代码是如何在分支上演进再合流的;而Rebase则让历史看上去像一条直线,没有分叉(就像所有改动都串行发生在一个分支上)。Rebase后的历史往往更加简洁、线性,使用 git log
浏览时不会被大量的合并提交干扰。但另一方面,Merge不改变已有历史,而Rebase通过重写历史来达成线性,可能会让历史记录失去分支上下文。
**使用注意:由于 rebase 会重写提交历史,所以有一个“变基黄金法则”**必须牢记:绝不要在公共分支上对已经推送的提交执行 rebase 。换句话说,仅对尚未分享给别人的本地分支使用 rebase。一旦你在协作的分支(比如 origin/main 或团队共享的feature分支)上做了变基,然后强制推送,会导致他人的仓库出现冲突和混乱(因为他们的提交历史与你的不一致)。因此,在共享环境下,rebase 只能用于更新自己本地的开发分支,再正常推送,或用于自己fork后在PR合并前整理提交。遵循“变基黄金法则”:绝不在公有仓库的分支上执行 rebase。
5.2、举例:使用 rebase 更新分支
假设你正在开发分支 feature-A
上工作,此时需要同步主分支 main
最新的更新。在 merge 和 rebase 两种做法中,如果选择 rebase:
# 确保主分支是最新的
git checkout main
git pull origin main
# 切回功能分支,执行变基
git checkout feature-A
git rebase main
执行上述操作后,Git 会将 feature-A
分支上的提交一个接一个地转移到 main
最新提交之后。如果没有冲突,变基会自动完成。若有冲突,Git 会停止并让你解决冲突,解决完毕后用 git add
将更改暂存,然后 git rebase --continue
继续剩下的提交。全部结束后,切换到 main,可以使用 git merge feature-A
快速合并(fast-forward)或者直接 git push origin feature-A:main
推送 feature-A 分支作为 main 分支的新更新(比如在pull request被接受时)。这样 main 的历史就是线性的,没有额外的分叉点。相比直接 merge,这种方法避免了合并提交,使历史更干净。
5.3、举例:交互式 rebase 整理提交
假如在开发一个功能过程中,你做了3次提交:第一次提交遗漏了一些文件,第二次补上遗漏,第三次修正了前面提交信息的一个拼写错误。最终这些其实都是为实现同一个功能,理想情况下可以合并成一个提交。使用 git rebase -i
可以实现:
git rebase -i HEAD~3
在打开的编辑界面中,你会看到类似:
pick f1a2b3c Implement feature X
pick a3d4e5f Add missing files for feature X
pick b4c6d7e Fix typo in feature X description
将后两行的 pick
改为 squash
(或缩写 s
),表示将它们压缩到第一个提交中。保存退出后,Git 会应用第一个提交,然后将后两个提交的改动合并进第一个。在这个过程中会让你编辑合并后的提交消息,你可以整合原先三个提交的信息为一条完整描述。完成后,历史中就只剩下一条提交,包含所有改动。这就是一次提交压缩操作。类似地,你可以重新排序提交顺序(调整行的先后)、丢弃某些提交(改为 drop
),或者标记 edit
手动修改某个提交的内容(在 rebase 过程中暂停让你修正然后继续),等等。
通过 rebase 的这些技巧,我们能够在不影响最终代码结果的情况下,让提交历史更符合阅读和维护的需要。例如,将“修复拼写错误”“调整格式”这类杂碎 commit 合并到主要 commit 中,保持主干历史的清晰。当然,这种修改历史的动作只应对自己本地分支进行,在与团队协作前完成整理。
6、推荐的 Git 使用流程与团队协作技巧
掌握了上述 Git 高级功能后,我们来讨论一下在团队协作中,如何高效地使用 Git 工作,以及一些值得遵循的实践规范。良好的协作流程可以避免很多问题,确保团队所有成员各司其职又步调一致。
下面是一套常见的 Git 团队协作开发流程(以功能开发为例):
-
同步主分支: 在开始新的工作之前,先切换到主分支(例如
main
或develop
),执行git pull
拉取远程最新代码。这样可以确保你的主分支是最新状态,为后续创建新分支打好基础。 -
新建功能分支: 从主分支创建一个新的功能分支用于开发你的任务:
git checkout -b feature/some-feature
分支命名应清晰反映工作内容,比如
feature/login-auth
或bugfix/issue-101
。在这个分支上进行你的开发工作,与此同时主分支的代码保持不变。 -
在分支上进行开发并频繁提交: 在功能分支上编写代码。建议遵循“早提交、勤提交”的原则,每实现一个独立的小功能或达到一个里程碑就使用
git add
和git commit
提交一次。提交信息要清晰描述修改目的,例如 “Add login form UI” 而不是 “update code”。频繁提交有助于记录开发轨迹,也使得出现问题时更容易定位和回溯。 -
将分支推送到远程: 虽然还在开发中,但也最好经常将本地分支推送到远程仓库备份,并让团队可见你的进展:
git push -u origin feature/some-feature
使用
-u
将本地分支与远程同名分支建立跟踪关系。之后如果继续有新的提交,可定期git push
更新远程。这样即使你的电脑发生故障,代码也不会丢失,同时同事也能看到并拉取你的最新代码(如果需要协作)。 -
与主分支保持同步: 开发过程中,主分支可能也在推进其他更新。为避免分支长期偏离主线,最好定期用主分支的变化更新你的功能分支。例如可以执行
git pull origin main
(或git fetch
然后在分支上git merge origin/main
)将主分支的修改合并过来。如果希望更整洁,可以用git rebase origin/main
将你的提交变基到主分支后。这能减少最终合并时的冲突概率。 -
提交合并请求(Pull Request)并代码审查: 功能开发完成并经过自测后,将你的分支代码合并回主分支。通常做法是在托管平台上创建一个 Pull Request(合并请求),描述你的更改,通知团队其他成员审核代码。代码审查(Code Review)可以发现潜在问题并确保代码质量。在 PR 通过后,维护者会将你的功能分支合并到主分支。合并可以采用 Squash Merge(压缩成一次提交)或者普通 Merge,根据团队约定。
-
清理分支并部署: 功能成功合并进主分支后,可以删除远程和本地的功能分支(以避免过多分支混乱仓库):
git branch -d feature/some-feature # 删除本地分支 git push origin --delete feature/some-feature # 删除远程分支
之后,在本地切回主分支并
git pull
获取最新的代码。如果需要部署或发布,可以根据主分支最新代码进行。
在上述流程中,有一些值得强调的团队协作技巧和最佳实践:
-
确保主分支始终可用: 主分支(如
main
/master
)通常代表产品的稳定版本。不在主分支上直接开发新功能,所有对主分支的修改都通过分支合并来完成。这样主分支上的代码始终是可以随时发布或部署的。如果需要紧急修复,也使用热修复分支来完成并尽快合并。 -
善用分支命名和标签: 清晰的分支命名有助于协作。例如见到
feature/login-auth
自然知道是开发登录认证功能。对于重要的里程碑或发布版本,使用 Git 标签(tag)来标记提交,方便以后查找对应版本的代码。 -
编写有意义的提交消息: 一个好的提交消息能够简洁概括更改内容或原因,方便日后查阅历史时了解每次改动的目的。避免使用含糊不清的描述如“修改代码”“更新了几个文件”。常见格式是在简短的标题后可选地补充细节描述,团队可以采用一致的 commit message 风格(比如遵循 Angular 提交信息规范等)。
-
及时拉取和合并: 团队协作中,每个人应经常同步远程仓库的最新变化(
git pull
),特别是在开始新的任务或准备将自己的修改推送前。这可以最大限度减少冲突。当发现与他人有冲突时,积极沟通,共同决定如何解决。 -
不要在公共分支上强制推送: 避免使用
git push --force
强制推送主分支或他人也在用的分支。如果必须(例如更正错误的历史),也要提前与团队说明,确保别人没有基于旧历史继续工作。强制推送可能覆盖他人的提交,通常只有仓库管理员在特殊情况下才执行,而且通常通过 Pull Request 来协同完成。 -
使用
.gitignore
保持仓库整洁: 在协作项目中,制定.gitignore
文件很重要,确保编译产物、临时文件、不需要纳入版本控制的文件不被提交。例如日志文件、IDE配置、依赖生成的文件等应被忽略。这可以减少不必要的冲突和仓库臃肿。 -
定期备份和维护仓库: 虽然远程仓库本身就是备份,但也可以克隆镜像仓库作为额外备份。对长期项目,定期清理无用的分支,避免仓库中累积过多过期分支。对于超大的历史(比如大量二进制文件意外提交),可以考虑使用 Git 工具(如 BFG Repo-Cleaner)清理,但这些都是进阶话题了。