简体中文 ▾ 主题 ▾ 最新版本 ▾ gitworkflows 最后更新于 2.35.0

名称

gitworkflows - Git 推荐工作流概览

概要

git *

描述

本文档旨在记录并说明用于 git.git 项目本身的一些工作流要素。许多理念具有通用性,但对于参与人数较少的小型项目,很少需要用到完整的工作流。

我们制定了一套规则供快速参考,并用正文尝试解释其背后的原因。请勿总是生搬硬套;相比于本手册页,你应当更看重你行为背后的正当理由。

分离更改

作为一般原则,你应该尝试将更改拆分为小的逻辑步骤,并提交其中的每一个。它们应该是连贯的,独立于任何后续提交即可工作,并通过测试套件等。这使得评审过程更加容易,并使历史记录对于以后的检查和分析(例如使用 git-blame[1]git-bisect[1])更加有用。

为了实现这一点,请尝试从一开始就将工作拆分为小步骤。将几个提交压缩(squash)在一起总是比将一个大提交拆分为几个要容易。不要害怕在过程中产生太小或不完美的步骤。在发布之前,你以后总是可以随时通过 git rebase --interactive 返回并编辑这些提交。你可以使用 git stash push --keep-index 来运行独立于其他未提交更改的测试套件;参见 git-stash[1] 的示例(EXAMPLES)部分。

管理分支

有两个主要工具可用于将一个分支的更改包含到另一个分支:git-merge[1]git-cherry-pick[1]

合并(Merge)具有许多优点,因此我们尝试仅通过合并来解决尽可能多的问题。拣选(Cherry-picking)偶尔仍然有用;请参阅下文的“向上合并”示例。

最重要的是,合并是在分支级别工作的,而拣选是在提交级别工作的。这意味着合并可以同样轻松地结转来自 1、10 或 1000 个提交的更改,这反过来意味着工作流可以更好地扩展到大量的贡献者(和贡献)。合并也更容易理解,因为合并提交是一个“承诺”,即其所有父级的所有更改现在都已包含在内。

当然权衡也是存在的:合并需要更仔细的分支管理。以下小节讨论了重点。

晋升 (Graduation)

随着给定功能从实验性变为稳定,它也会在软件相应的分支之间“晋升”。git.git 使用以下集成分支

  • maint 追踪应进入下一个“维护版本”的提交,即上一个发布的稳定版本的更新;

  • master 追踪应进入下一个正式版本的提交;

  • next 旨在作为测试分支,用于测试 master 分支候选功能的稳定性。

还有第四个官方分支,其用途略有不同

  • seen(维护者看到的补丁)是一个集成分支,用于存放尚未准备好包含的东西(见下文“集成分支”)。

这四个分支中的每一个通常都是其上方分支的直接后代。

从概念上讲,功能从不稳定分支(通常是 nextseen)进入,并在被认为足够稳定后“晋升”到 master 以用于下一个版本。

向上合并

然而,上述“向下晋升”不能通过实际的向下合并来完成,因为那会将不稳定分支上的所有更改合并到稳定分支中。因此有以下

规则:向上合并

始终将你的修复提交到需要它们的、受支持的最旧分支。然后(定期)将集成分支逐级向上合并。

这提供了非常受控的修复流。如果你注意到已将一个修复应用于例如 master,但 maint 也需要该修复,则需要将其向下拣选(使用 git-cherry-pick[1])。这种情况会发生几次,除非你非常频繁地这样做,否则无需担心。

主题分支

任何非平凡的功能都需要多个补丁来实现,并且在其生命周期内可能会获得额外的错误修复或改进。

直接在集成分支上提交所有内容会导致许多问题:错误的提交无法撤销,因此必须逐个回滚,这会产生混乱的历史记录,并且当你忘记回滚一组更改的一部分时会产生进一步的错误风险。并行工作会混合更改,造成进一步的混乱。

使用“主题分支”可以解决这些问题。这个名字是不言自明的,但有一个来自上述“向上合并”规则的注意事项

规则:主题分支

为每个主题(功能、错误修复等)创建一个侧分支。从你最终想要将其合并到的最旧集成分支处分叉出来。

然后可以非常自然地完成许多事情

  • 要将功能/错误修复引入集成分支,只需合并它即可。如果主题在此期间有进一步发展,请再次合并。(请注意,你不一定必须先将其合并到最旧的集成分支。例如,你可以先将错误修复合并到 next,给它一些测试时间,并在知道它稳定后合并到 maint。)

  • 如果你发现需要来自 other 分支的新功能来继续处理你的主题,请将 other 合并到 topic。(但是,不要“只是出于习惯”这样做,见下文。)

  • 如果你发现自己从错误的分支分叉并想要将其“回溯时间”,请使用 git-rebase[1]

请注意,最后一点与前两点冲突:已合并到其他地方的主题不应被变基(rebase)。参见 git-rebase[1] 中关于“从上游变基中恢复”(RECOVERING FROM UPSTREAM REBASE)的部分。

我们应该指出,“习惯性地”(经常且没有真正理由)将集成分支合并到你的主题分支中——以及引申开来,定期将任何上游内容合并到任何下游内容中——是不被赞成的

规则:仅在明确的时间点合并到下游

除非有充分理由,否则不要合并到下游:上游 API 更改影响了你的分支;你的分支不再能干净地合并到上游等。

否则,被合并的主题会突然包含不止一个(分离开的)更改。由此产生的许多小型合并将极大地污染历史记录。以后调查文件历史的任何人都必须弄清楚该合并是否影响了开发中的主题。上游甚至可能无意中被合并到“更稳定”的分支中。以此类推。

抛弃型集成

如果你遵循了上一段,你现在将拥有许多小的主题分支,并偶尔想知道它们是如何相互作用的。也许合并它们的结果甚至无法工作?但另一方面,我们要避免将它们合并到任何“稳定”的地方,因为这种合并无法轻易撤销。

解决方案当然是进行一次我们可以撤销的合并:合并到一个抛弃型分支中。

规则:抛弃型集成分支

要测试多个主题的交互,请将它们合并到一个抛弃型分支中。绝对不能在这样的分支上开展任何工作!

如果你(非常)明确地表示该分支将在测试后立即删除,你甚至可以发布此分支,例如让测试人员有机会使用它,或让其他开发人员有机会查看他们正在进行的工作是否兼容。git.git 有一个这样的官方抛弃型集成分支,叫做 seen

发布的分支管理

假设你使用的是上述的合并方法,当你发布项目时,你需要执行一些额外的分支管理工作。

功能版本是从 master 分支创建的,因为 master 追踪应进入下一个功能发布的提交。

master 分支应该是 maint 的超集。如果此条件不成立,则 maint 包含一些未包含在 master 上的提交。因此,这些提交所代表的修复将不会包含在你的功能版本中。

要验证 master 确实是 maint 的超集,请使用 git log

配方:验证 mastermaint 的超集

git log master..maint

此命令不应列出任何提交。否则,请检出 master 并将 maint 合并到其中。

现在你可以继续创建功能版本。在 master 的末端应用一个标签,指明发布版本号

配方:发布打标签

git tag -s -m "Git X.Y.Z" vX.Y.Z master

你需要将新标签推送到公开的 Git 服务器(见下文“分布式工作流”)。这使得追踪你项目的其他人可以使用该标签。推送还可以触发 post-update 钩子来执行与发布相关的项目,例如构建发布源码包和预格式化的文档页面。

类似地,对于维护版本,maint 正在追踪要发布的提交。因此,在上述步骤中只需对 maint 而非 master 打标签并推送即可。

功能发布后的维护分支管理

功能发布后,你需要管理你的维护分支。

首先,如果你希望继续为最近一个版本之前的旧功能版本发布维护修复,那么你必须创建另一个分支来追踪该旧版本的提交。

为此,将当前的维护分支复制到另一个以先前发布版本号命名的分支(例如 maint-X.Y.(Z-1),其中 X.Y.Z 是当前版本)。

配方:复制 maint

git branch maint-X.Y.(Z-1) maint

maint 分支现在应该快进到新发布的逻辑,以便追踪当前版本的维护修复

配方:将 maint 更新到新版本
  • git checkout maint

  • git merge --ff-only master

如果合并失败,因为它不是快进(fast-forward),那么可能在功能版本中遗漏了 maint 上的一些修复。如果按照上一节所述验证了分支内容,则不会发生这种情况。

功能发布后 next 和 seen 的分支管理

功能发布后,集成分支 next 可以选择性地回退并从 master 的末端重新构建,方法是使用 next 上幸存的主题

配方:回退并重新构建 next
  • git switch -C next master

  • git merge ai/topic_in_next1

  • git merge ai/topic_in_next2

  • …​

这样做的好处是 next 的历史记录将保持干净。例如,某些合并到 next 中的主题最初看起来很有希望,但后来发现不理想或不成熟。在这种情况下,该主题会从 next 中回滚,但其曾经被合并和回滚的事实仍保留在历史记录中。通过重新创建 next,你可以给此类主题的另一个化身一个重新尝试的干净起点,而功能发布是执行此操作的一个很好的时间点。

如果你这样做,那么你应该发布一个公共公告,指出 next 已被回退并重新构建。

对于 seen 分支,可以遵循相同的回退和重建过程。由于 seen 是一个抛弃型分支,如上所述,因此不需要发布公告。

分布式工作流

在上一节之后,你应该知道如何管理主题了。通常,你不会是项目中唯一工作的人,因此你必须分享你的工作。

粗略地说,有两种重要的工作流:合并(merge)和补丁(patch)。重要的区别在于合并工作流可以传播完整的历史记录(包括合并),而补丁则不能。两种工作流可以并行使用:在 git.git 中,只有子系统维护者使用合并工作流,而其他所有人发送补丁。

请注意,维护者可能会施加限制,例如“Signed-off-by”要求,所有提交/补丁提交以供包含时必须遵守。请查阅你项目的文档以获取更多信息。

合并工作流

合并工作流通过在上游和下游之间复制分支来工作。上游可以将贡献合并到官方历史记录中;下游将其工作基于官方历史记录。

有三个主要工具可用于此

  • git-push[1] 将你的分支复制到远程仓库,通常是所有相关方都可以读取的仓库;

  • git-fetch[1] 将远程分支复制到你的仓库;以及

  • git-pull[1] 一次性完成获取(fetch)和合并(merge)。

注意最后一点。除非你真的想要合并远程分支,否则不要使用 git pull

分发更改很容易

配方:Push/pull: 发布分支/主题

git push <remote> <branch> 并告诉大家他们可以从哪里获取。

你仍然需要通过其他方式(例如邮件)告诉人们。(Git 提供了 git-request-pull[1] 来向上游维护者发送预格式化的拉取请求,以简化此任务。)

如果你只想获取集成分支的最新副本,保持更新也很容易

配方:Push/pull: 保持更新

使用 git fetch <remote>git remote update 来保持更新。

然后按照之前解释的方法,从稳定的远程分支分叉你的主题分支。

如果你是维护者,并且想要将他人的主题分支合并到集成分支中,他们通常会通过邮件发送合并请求。这样的请求看起来像

Please pull from
    <URL> <branch>

在这种情况下,git pull 可以一次性完成获取和合并,如下所示。

配方:Push/pull: 合并远程主题

git pull <URL> <branch>

偶尔,维护者在尝试从下游拉取更改时可能会遇到合并冲突。在这种情况下,他们可以要求下游自己进行合并并解决冲突(也许他们更清楚如何解决冲突)。这是下游应该从上游合并的极少数情况之一。

补丁工作流

如果你是以电子邮件形式向上游发送更改的贡献者,你应该像平常一样使用主题分支(见上文)。然后使用 git-format-patch[1] 生成相应的电子邮件(强烈建议不要手动格式化,因为这能减轻维护者的负担)。

配方:format-patch/am: 发布分支/主题
  • git format-patch -M upstream..topic 将它们转换为预格式化的补丁文件

  • git send-email --to=<recipient> <patches>

有关进一步的使用说明,请参阅 git-format-patch[1]git-send-email[1] 手册页。

如果维护者告诉你你的补丁不再适用于当前上游,你将不得不变基(rebase)你的主题(你不能使用合并,因为你不能用 format-patch 格式化合并)

配方:format-patch/am: 保持主题更新

git pull --rebase <URL> <branch>

然后你可以在变基期间修复冲突。假设你除了通过邮件之外没有发布过你的主题,因此变基它不是问题。

如果你收到这样一系列补丁(作为维护者,或者作为发送补丁的邮件列表的读者),请将邮件保存为文件,创建一个新的主题分支并使用 git am 导入提交

配方:format-patch/am: 导入补丁

git am < patch

值得指出的一项功能是三路合并,如果你遇到冲突,它可以提供帮助:git am -3 将使用补丁中包含的索引信息来计算合并基准。有关其他选项,请参见 git-am[1]

GIT

Git[1] 套件的一部分