目录
-
- 前言
- 模拟一:单一共享分支协作模式
-
- [1.1 环境准备与分支创建](#1.1 环境准备与分支创建)
- [1.2 开发者1的本地环境同步](#1.2 开发者1的本地环境同步)
- [1.3 开发者2的初始设置](#1.3 开发者2的初始设置)
- [1.4 开发者1的编码与推送](#1.4 开发者1的编码与推送)
- [1.5 开发者2的操作与冲突解决](#1.5 开发者2的操作与冲突解决)
- [1.6 将功能分支合并到主干](#1.6 将功能分支合并到主干)
- [1.7 清理工作](#1.7 清理工作)
- [1.8 单一分支协作总结](#1.8 单一分支协作总结)
- 模拟二:多分支(功能分支)协作模式
-
- [2.1 开发者1创建并推送功能分支](#2.1 开发者1创建并推送功能分支)
- [2.2 开发者2创建并推送功能分支](#2.2 开发者2创建并推送功能分支)
- [2.3 跨分支协作场景(工作交接)](#2.3 跨分支协作场景(工作交接))
- [2.4 通过 Pull Request 合并分支](#2.4 通过 Pull Request 合并分支)
- [解决git branch -a 打印已被删除的远程分支的方法](#解决git branch -a 打印已被删除的远程分支的方法)
- 结论
前言
在现代软件开发中,版本控制系统(Version Control System, VCS)是团队协作的基石,而 Git 凭借其分布式特性、强大的分支管理能力和高效的性能,已成为事实上的行业标准。掌握 Git 不仅仅是知道 add、commit、push、pull 这几个基本命令,更关键的是理解其背后的协作模型和工作流程。本文将通过两个详尽的模拟场景,深入探讨 Git 在多人协作环境下的两种核心工作模式,并最终介绍如何进行仓库的日常维护,确保每一位开发者都能在一个清晰、高效的环境中工作。
模拟一:单一共享分支协作模式
这种模式通常应用于小型团队或快速迭代的简单项目中,团队成员在同一个功能分支上进行开发。虽然直接,但它也更容易引发冲突,对团队成员的沟通和操作时机有更高要求。
目标: 在 master 分支下的 fille.txt 文件中,最终新增两行内容:"aaa" 和 "bbb"。
实现: 由开发者1新增 "aaa",开发者2新增 "bbb"。
条件: 两位开发者在同一个名为 dev 的共享分支下完成各自的开发任务。
1.1 环境准备与分支创建
协作的第一步是建立一个共享的工作空间,也就是一个专门用于本次功能开发的分支。通常,这个分支会由项目负责人或其中一位开发者在远程仓库(如 Gitee、GitHub)上先行创建。

上图展示了在 Gitee 的网页界面上创建新分支的操作。从 master 分支创建了一个名为 dev 的新分支。这是一个非常普遍的起点,确保了我们的功能开发是基于一个稳定、干净的主线版本。
1.2 开发者1的本地环境同步
假设开发者1已经克隆了该项目。此时,其本地仓库并不知道远程仓库刚刚发生的变化(即新建了 dev 分支)。我们需要通过一系列命令来验证并同步这个状态。
首先,查看本地存在哪些分支。
bash
git branch

执行 git branch 命令后,输出结果只显示了 master 分支,并且星号 * 表明当前工作区正处于 master 分支。这证实了本地仓库对远程新建的 dev 分支一无所知。
接着,我们查看本地仓库所记录的远程分支信息。
bash
git branch -r

使用 -r(--remotes)参数,我们可以看到本地缓存的远程分支列表。输出 origin/master 和 origin/HEAD -> origin/master,同样没有 dev 分支的踪迹。这说明本地的远程分支信息也已经过时。
为了获取远程仓库的最新状态,需要执行 git pull 或 git fetch。git pull 是一个复合命令,相当于 git fetch(从远程抓取最新信息)后跟 git merge(将远程分支合并到当前本地分支)。在当前场景下,我们在 master 分支上,执行 git pull 会尝试抓取所有远程更新,并更新本地的 master 分支。
bash
git pull

上图的输出信息非常关键。From gitee.com:caijiuuyk/mypicture 表明了数据来源。* [new branch] dev -> origin/dev 是最重要的信息,它明确指出 Git 发现了一个新的远程分支 dev,并已在本地创建了一个对应的远程跟踪分支 origin/dev。Already up to date. 则表示本地的 master 分支与远程的 master 分支内容一致,无需合并。
现在,我们再次检查所有分支,以确认 dev 分支已被本地 Git 识别。
bash
git branch -a

使用 -a(--all)参数,可以列出所有本地分支和远程跟踪分支。从图中可以看到,除了本地的 master 和远程的 origin/master,现在列表中明确出现了红色的 remotes/origin/dev。这标志着开发者1的本地环境已经成功与远程仓库同步,可以开始在 dev 分支上进行工作了。
1.3 开发者2的初始设置
为了模拟真实协作,我们为开发者2准备一个全新的工作环境,即重新克隆整个仓库。

当一个新的开发者通过 git clone 命令克隆一个仓库时,Git 会自动将远程仓库的所有分支信息下载到本地,并创建一个默认的 master(或 main)本地分支来跟踪 origin/master。此时,开发者2的本地仓库同样拥有了 origin/dev 的信息。
1.4 开发者1的编码与推送
开发者1现在需要在 dev 分支上添加内容 "aaa"。
首先,需要创建一个本地的 dev 分支,并切换过去。同时,为了方便后续的 push 和 pull 操作,最好让这个本地分支直接与远程的 origin/dev 分支建立"跟踪"(tracking)关系。
bash
git checkout -b dev origin/dev

这个命令是一个非常高效的快捷方式,它完成了三件事:
git branch dev:创建一个名为dev的新本地分支。git checkout dev:切换到这个新创建的dev分支。- 设置本地
dev分支跟踪(track)远程的origin/dev分支。
我们可以通过 git branch -vv 命令来验证这种跟踪关系。
bash
git branch -vv

-vv 参数提供了更详细的分支信息。输出中的 [origin/dev] 部分明确地告诉我们,本地的 dev 分支正在跟踪 origin/dev。这意味着当在此分支上执行 git push 或 git pull 时,Git 会自动知道目标是 origin/dev。
接下来,开发者1修改 fille.txt 文件,添加 "aaa"。

修改完成后,执行标准的提交流程:git add 将文件更改添加到暂存区,git commit 将暂存区的更改记录为一个新的提交。

本地提交完成后,开发者1需要将这些更改推送到远程共享的 dev 分支,以便开发者2可以看到。

由于之前已经建立了跟踪关系,所以可以直接使用 git push。Git 成功地将本地 dev 分支的提交推送到了 origin/dev。
在 Gitee 网页上刷新并切换到 dev 分支,可以看到 fille.txt 文件已经包含了 "aaa" 这行内容,证明开发者1的工作已成功同步到云端。

1.5 开发者2的操作与冲突解决
现在轮到开发者2了。开发者2的目标是在 fille.txt 中添加 "bbb"。
开发者2首先也需要一个本地的 dev 分支。这里演示一种与开发者1不同的创建方式。
bash
git checkout -b dev
git branch -vv

git checkout -b dev 仅仅是基于当前分支(这里是 master)创建了一个新的本地分支 dev 并切换过去。从 git branch -vv 的输出中可以看到,这个新建的 dev 分支后面没有 [origin/dev] 标记,说明它没有与任何远程分支建立跟踪关系。
在这种状态下尝试 git pull,希望获取开发者1的更新,会发生什么呢?
bash
git pull

Git 提示了一个非常常见的错误信息:"There is no tracking information for the current branch." (当前分支没有跟踪信息)。这是因为 Git 不知道本地的 dev 分支应该从远程的哪个分支拉取数据。Git 在提示中还给出了解决方案:执行 git branch --set-upstream-to=origin/<branch> dev 来建立连接。
我们按照提示操作,将本地 dev 分支与远程 origin/dev 分支关联起来。
bash
git branch --set-upstream-to=origin/dev dev
git branch -vv

命令执行成功,再次用 git branch -vv 检查,可以看到 [origin/dev] 已经出现,跟踪关系建立成功。
然而,开发者2此时忘记了执行 git pull 来同步开发者1的最新代码 ,直接在自己过时的版本上开始修改,向 fille.txt 添加了 "bbb"。

修改后,开发者2进行 add 和 commit,然后尝试 push。

推送被远程服务器拒绝(rejected)。这是多人协作中极其常见的场景。错误提示 updates were rejected because the remote contains work that you do not have locally 明确地指出了原因:远程 dev 分支包含了本地所没有的提交(即开发者1添加 "aaa" 的提交)。Git 为了防止意外覆盖他人的工作,强制要求开发者必须先将远程的变更合并到本地,解决所有差异后,才能推送。
遵从 Git 的指示,开发者2执行 git pull。
bash
git pull

git pull 尝试自动合并远程的 origin/dev 分支。但是,由于开发者1和开发者2都修改了 fille.txt 文件的同一区域,Git 无法自动决定最终应该保留哪个版本,于是报告了一个合并冲突(Merge conflict)。终端输出 CONFLICT (content): Merge conflict in fille.txt 和 Automatic merge failed; fix conflicts and then commit the result.,清晰地指明了冲突文件和下一步操作。
此时,打开 fille.txt 文件,会看到 Git 插入的冲突标记。

冲突标记的含义如下:
<<<<<<< HEAD:这之下到=======之前的内容,是当前本地分支(HEAD)的修改,即开发者2添加的 "bbb"。=======:分割线,区分本地修改和远程修改。>>>>>>> ...:这之上到=======之后的内容,是从远程拉取下来的、导致冲突的修改,即开发者1添加的 "aaa"。
解决冲突需要开发者手动编辑该文件,移除冲突标记,并根据业务需求整合代码,形成最终的正确版本。在这个场景中,目标是同时保留 "aaa" 和 "bbb"。

修改文件并保存后,需要告知 Git 冲突已经解决。首先,查看当前仓库的状态。
bash
git status

git status 的输出显示了 Unmerged paths(未合并的路径),提示我们需要通过 git add <file>... 来标记冲突已解决。按照这个流程,我们将解决后的 fille.txt 添加到暂存区,然后创建一个新的"合并提交"(merge commit)。

在上图中,开发者2执行了 git add 和 git commit(Git 在 pull 冲突后会自动生成一个默认的合并提交信息,可以直接使用),然后再次执行 git push。这一次,由于本地 dev 分支的历史记录已经包含了远程 dev 分支的所有历史,并在此基础上新增了一个合并提交,所以推送成功了。
此时,远程的 dev 分支已经达到了我们的预期目标。

1.6 将功能分支合并到主干
dev 分支的开发任务已经完成,下一步是将其成果合并回 master 主干分支。通常有两种方式:
-
发起 Pull Request (PR) 或 Merge Request (MR) :这是团队协作中最推荐的方式。开发者在 Gitee/GitHub 等平台上创建一个 PR,请求将
dev分支合并到master。这会启动一个代码审查(Code Review)流程,团队其他成员可以检查代码、提出修改意见。只有在审查通过后,代码才会被合并。这确保了主干代码的质量。
-
本地直接合并:如果项目流程允许,或者是由项目负责人操作,也可以在本地完成合并再推送到远程。
这里我们演示第二种方式:本地合并。一个健壮的合并流程如下:
最佳实践: 在将功能分支(dev)合并到目标分支(master)之前,应先将最新的目标分支合并到功能分支,以在功能分支内部解决可能存在的冲突。这可以保持 master 分支的提交历史干净,避免在 master 上出现复杂的合并冲突。
我们回到开发者1的终端来完成这个操作。
首先,确保本地的 master 分支是最新版本。

上图显示开发者1切换到了 master 分支,并执行了 git pull,确保与远程 master 同步。
然后,切换回 dev 分支,并将最新的 master 合并进来。

在这个简单的场景中,master 分支自 dev 分支创建以来没有任何新的提交,所以 git merge master 的结果是 Already up to date,没有产生任何变化。但在真实项目中,这一步非常重要,可能会需要解决冲突。
最后,切换到 master 分支,执行最终的合并。

执行 git merge dev,Git 会将 dev 分支的所有提交应用到 master 分支上。这里使用了 Fast-forward 模式,因为 master 分支的指针可以直接向前移动到 dev 分支的最新提交位置。
合并完成后,本地 master 分支已经包含了所有更改,最后一步是将其推送到远程仓库。

1.7 清理工作
功能开发和合并都已完成,dev 分支的历史使命也就结束了。为了保持仓库的整洁,应该删除这个不再需要的分支。可以在 Gitee 网页上删除远程分支。

同时,开发者也应该删除各自的本地 dev 分支,使用 git branch -d dev。
最终,在 master 分支上查看 fille.txt 文件,可以看到 "aaa" 和 "bbb" 都已存在,任务圆满完成。

1.8 单一分支协作总结
这种工作模式的核心流程可以概括为:
- 尝试直接
git push推送本地修改。 - 如果推送失败,说明远程分支有更新,必须先执行
git pull。 git pull可能会导致合并冲突,此时需要手动解决冲突文件。- 解决冲突后,通过
git add和git commit完成合并提交。 - 再次
git push,此时应该会成功。 - 功能完成后,将该分支合并到
master,然后删除分支。
模拟二:多分支(功能分支)协作模式
这是目前业界最主流、最推荐的协作模式,也常被称为"Git Flow"或"GitHub Flow"的简化实践。每个开发者为自己负责的功能或修复的 Bug 单独创建一个分支,开发工作在各自的分支中隔离进行,完成后通过 Pull Request 合并回主干。
目标: 在 master 分支下,新增 function1.txt 和 function2.txt 两个文件。
实现: 由开发者1在 feature-1 分支上创建 function1.txt,开发者2在 feature-2 分支上创建 function2.txt。
条件: 两位开发者在完全隔离的两个不同分支下协作。
2.1 开发者1创建并推送功能分支
开发者1的任务是创建 function1.txt。他将为此创建一个名为 feature-1 的分支。
bash
git checkout -b feature-1

该命令从当前的 master 分支创建了 feature-1 并切换过去。
开发者1在 feature-1 分支上创建 function1.txt 文件,写入内容,然后进行 add 和 commit。

当开发者1尝试 git push 时,会遇到一个问题。

Git 报错 fatal: The current branch feature-1 has no upstream branch.。这是因为 feature-1 是一个纯本地分支,远程仓库并不知道它的存在,因此 Git 不知道该将它推送到哪里。
解决方案是明确告诉 Git 推送的目标,并建立跟踪关系。
bash
git push origin feature-1

这个命令的含义是:将本地的 feature-1 分支推送到名为 origin 的远程仓库,并在远程也创建一个名为 feature-1 的分支。输出中的 [new branch] 表明远程成功创建了新分支。
(注:更常用的命令是 git push -u origin feature-1,-u 或 --set-upstream 参数会在推送的同时自动设置好本地分支与远程分支的跟踪关系。)
我们可以通过 git branch -a 来确认远程跟踪分支已经建立。

remotes/origin/feature-1 的出现证实了这一点。
在 Gitee 网页上也可以看到这个新分支以及 function1.txt 文件。

至此,开发者1的任务初步完成。
2.2 开发者2创建并推送功能分支
现在轮到开发者2,他的任务是创建 function2.txt。
首先,开发者2需要确保自己的工作是基于最新的主干代码。这是一个至关重要的习惯。

开发者2先用 git branch -a 查看了所有分支,发现自己还在之前的 dev 分支上。
他切换回 master 分支。
bash
git checkout master

然后执行 git pull,同步远程 master 分支可能存在的任何更新。
bash
git pull

在最新的 master 基础上,开发者2创建自己的功能分支 feature-2。
bash
git checkout -b feature-2

接着,开发者2创建 function2.txt 文件,添加内容,并进行 add 和 commit。

与开发者1一样,他也需要将这个新的本地分支推送到远程。
bash
git push origin feature-2

推送成功后,远程仓库现在同时存在 feature-1 和 feature-2 两个功能分支,它们各自独立开发,互不影响。

2.3 跨分支协作场景(工作交接)
我们模拟一个真实场景:开发者2突然生病,需要开发者1接手 feature-2 分支继续开发。

开发者1的本地仓库目前并没有 feature-2 这个本地分支,只知道远程存在 origin/feature-2。他需要先获取远程的最新信息。
bash
git pull

开发者1当前在 feature-1 分支上,他执行 git pull 时,Git 提示 feature-1 没有设置上游分支(因为他之前创建时没有用 -u 参数)。尽管有这个提示,但 git pull 的 fetch 部分仍然成功执行了,它从远程抓取了所有分支的最新信息,包括 feature-2 的存在。
通过 git branch -a 可以看到,remotes/origin/feature-2 已经出现在列表中。

现在,开发者1需要创建一个本地的 feature-2 分支来跟进这项工作。
bash
git checkout -b feature-2 origin/feature-2
这个命令我们之前见过,它会创建一个本地的 feature-2 分支,并自动设置为跟踪 origin/feature-2。
开发者1接手后,对 function2.txt 进行了修改,并提交、推送。

几天后,开发者2康复归来,他需要继续 feature-2 的工作。他的本地 feature-2 分支还是他生病前的状态,已经落后于远程。他尝试 git pull 同步。

由于开发者2当初创建 feature-2 时也没有建立跟踪关系,所以他也收到了同样的"no upstream branch"错误。
他需要先建立跟踪关系。
bash
git branch --set-upstream-to=origin/feature-2 feature-2

建立关系后,再次 git pull,成功将开发者1所做的修改同步到了本地。他在此基础上完成了最后的开发。

最后,开发者2将最终版本提交并推送到远程 feature-2 分支。

2.4 通过 Pull Request 合并分支
现在,feature-1 和 feature-2 的开发工作都已完成,是时候将它们合并到 master 了。我们将使用 Pull Request 来完成这个过程。
首先,开发者2为 feature-2 分支发起一个 Pull Request,目标是合并到 master。

项目负责人或其他团队成员会对这个 PR 进行代码审查。

审查通过后,点击"合并"按钮。

Gitee 会执行合并操作,并在 master 分支上创建一个合并提交。

此时,master 分支已经包含了 function2.txt。

接下来轮到合并 feature-1。此时,master 分支已经因为合并了 feature-2 而向前演进了。feature-1 分支现在是基于一个"旧"的 master 创建的。直接合并 feature-1 可能会在 master 分支上产生复杂的合并历史,甚至冲突。
最佳实践 :在为功能分支创建 PR 之前,应先将最新的 master 分支合并到该功能分支中,确保功能分支是基于最新的主干代码。
开发者1来执行这个操作。首先,同步并更新本地的 master 分支,以获取 feature-2 被合并后的最新状态。
bash
git checkout master
git pull

然后,切换到 feature-1 分支,并将 master 合并进来。
bash
git checkout feature-1
git merge master

由于 feature-1 和更新后的 master(包含了 function2.txt)修改的是不同的文件,所以没有冲突。Git 自动创建了一个合并提交。执行 git merge 后,通常会弹出一个文本编辑器,让用户编辑合并提交的信息,保存并退出即可。

现在,本地的 feature-1 分支既包含了它自己的功能(function1.txt),也包含了来自 master 的最新更新(function2.txt)。将这个更新后的 feature-1 推送到远程。


此时,远程的 feature-1 分支已经与 master 完全同步,并且包含了自身的开发成果。现在为它发起 PR,将会是一个非常干净、无冲突的合并。

合并这个 PR。

最终,master 分支上成功地包含了 function1.txt 和 function2.txt,并且整个协作过程清晰、隔离、可控。

解决git branch -a 打印已被删除的远程分支的方法
在功能分支被合并和删除后,我们的远程仓库已经很干净了。但在本地,情况可能并非如此。
执行 git branch -a 查看所有分支。

可以看到,即使远程的 dev, feature-1, feature-2 分支都已被删除,本地的 remotes/origin/... 列表中依然保留着它们的记录。这些被称为"过时"的远程跟踪分支。虽然它们不影响正常工作,但会造成列表冗余,影响可读性。
我们可以使用 git remote show origin 命令来查看 origin 这个远程仓库的详细信息,它会明确指出哪些分支已经过时。
bash
git remote show origin

在输出中,Stale tracking branches 部分明确列出了 dev, feature-1, feature-2 是过时的。
要清理这些过时的本地记录,可以使用 git remote prune origin 命令。
bash
git remote prune origin

prune 的意思是"修剪",这个命令会删除所有在本地存在、但在 origin 远程上已不存在的远程跟踪分支。
清理完成后,再次执行 git branch -a。
bash
git branch -a

现在列表变得非常干净,只剩下仍然存在的 master 分支。这是一种良好的仓库维护习惯,能让协作者对项目的当前分支结构有清晰的认识。
(注:一个更便捷的方式是使用 git fetch --prune 或 git fetch -p,它可以在每次从远程拉取信息时,自动清理掉过时的远程跟踪分支。)
结论
本文通过两个详尽的模拟实验,展示了 Git 的两种核心多人协作模式。
- 单一共享分支模式 简单直接,适用于小型、快速的项目,但对开发者的操作同步性和沟通要求较高,冲突风险也更大。
- 多分支(功能分支)模式 提供了极佳的隔离性,每个功能都在独立的环境中开发,通过 Pull Request 机制引入了代码审查环节,极大地保障了主干代码的质量和稳定性。它通过"先更新再合并"的原则,将冲突解决的责任下放到功能分支内部,保证了
master分支的整洁与线性。这是当今绝大多数团队推荐并采用的标准化工作流程。
无论采用哪种模式,清晰的沟通、统一的规范和对 Git 工作流的深刻理解都是高效协作的关键。掌握这些模式,并辅以如 git remote prune 这样的仓库维护技巧,将使团队的开发流程更加顺畅、代码库更加健康。