使用 git rebase 和 git merge 建立良好的团队工作流程
Using git rebase and git merge to set up a good team workflow
我知道有很多类似的问题,但我找不到一个好的答案来帮助我在我工作的公司提出一个好的解决方案。我们开发人员不多,但我想提出一个可扩展的工作流程。
(并非如此)假设情况
这种情况是最常见的:有一个 master
分支从不接收直接提交。如果我需要做某事,我会创建一个 feature/personal
分支(通常长期使用的个人分支比胖功能分支更常用)。
一旦我对我创建的代码感到满意,我想将它带回 master
(与此同时,它已经收到另一个提交)。
重要的是要指出 master
和 branchX
总是被推送到远程。
因此,为了以图形方式阐明它,我们处于这种情况(我将使用 C 进行提交,M 进行合并) :
branch1 C2---C3---C6---C9---
/
master C0---C1---C4---C5---C7---C8
当前工作流程
可以定义当前使用的工作流程merge-up/merge-down:因为我不想修复master中的合并冲突,所以首先我merge- up master
inside branch1
, 然后我 merge-down branch1
in master
.
branch1 C2---C3---C6---C9---M1
/ /\
master C0---C1---C4---C5---C7---C8 M2
这样做,我解决了分支内部的冲突,然后我可以将我的分支合并到 master 中。
我个人不喜欢这个解决方案,主要原因有两个:
- 它导致一个非常混乱的历史树
- 将父分支合并到子分支对我来说听起来毫无意义
另一方面,我的同事认为:
- 它很容易理解,即使是非专业的实习同事也是如此
- 它会导致更少的错误,因为您没有触及历史记录(与 rebase 不同)
建议的解决方案
我提出的是常见且更直接的 rebase-before-merge-down 工作流程。
一旦我想在 master
中合并 branch1
,首先我 rebase 前者在后者上,所以我处理我分支中的所有冲突;比我 向下合并 branch1
到 master
(如果功能分支有意义,则使用 NO-FF,否则使用 FF)
branch1 C2---C3---C6
rebase / \
master C5---C7---C8 M1
This solution, however, has a main drawback:
Since both are synced with the remote, a git push --force
is necessary. So, if someone do something wrong (because he's in a hurry, distracted or silly), weeks of work could be lost in one second.
另一方面,优点应该是:
- 保持历史树非常干净和有意义
- 展平并删除无用的分支,只保留相关的分支
所以,问题是?
您在大型团队中采用哪种可扩展的工作流程来保持 git 历史的清晰和有意义,另一方面,防止像错误 git push --force
那样的潜在灾难?
根据我的经验,一个常见的模型是这样的:
- 有一个权威的 master 存储库。
- 这里的
master
分支只能通过拉取请求更新(用你的话说,向下合并)。
- 是否可以强制推送到其他分支是临时决定的(往往取决于实际需要同时在一个分支上工作的人数)。
- 每个人都维护自己的主存储库分支。
- 他们可以随时强制推送到自己的分支。
- 要将更改合并到 master 存储库的
master
分支,您需要将分支变基到 master
并拉入请求。
版本处理起来要棘手得多。
我建议查看 Linux、Git、Node 等开源项目如何维护其存储库并将其考虑在内。
如果branchX
(例如branch1
)适合你自己,你可以使用你想要的方式:合并前变基。
如果branchX
(比如branch1
)适用于所有开发者,你最好不要使用你的前置解决方案,你说的主要缺点是其他开发者会感到困惑,他们可能再也找不到自己的变化了。
有一种方法可以使用:git merge branch1 --squash
。
假设您 git 日志是:
branch1 C2---C3---C6---C9---
/
master C0---C1---C4---C5---C7---C8
执行 git merge branch1 --squash
后,git 日志将如下所示:
branch1 C2---C3---C6---C9
/
master C0---C1---C4---C5---C7---C8---M1
这使您的 master
分支看起来更清晰。如果你想开发新功能,你可以直接从 master
签出功能分支。
顺便说一句:我需要更正您在当前工作流程中使用的图表。
将 master
合并到 branch1
后,图形看起来像
C2---C3---C6---C9---M1 branch1
/ /
C0---C1---C4---C5---C7---C8 master
将branch1
合并到master
后,默认不会创建提交M2
,它只是一个快进合并。 branch1
和 master
都指向 M1
现在提交。
C2---C3---C6---C9---M1 branch1, master
/ /
C0---C1---C4---C5---C7---C8
我知道有很多类似的问题,但我找不到一个好的答案来帮助我在我工作的公司提出一个好的解决方案。我们开发人员不多,但我想提出一个可扩展的工作流程。
(并非如此)假设情况
这种情况是最常见的:有一个 master
分支从不接收直接提交。如果我需要做某事,我会创建一个 feature/personal
分支(通常长期使用的个人分支比胖功能分支更常用)。
一旦我对我创建的代码感到满意,我想将它带回 master
(与此同时,它已经收到另一个提交)。
重要的是要指出 master
和 branchX
总是被推送到远程。
因此,为了以图形方式阐明它,我们处于这种情况(我将使用 C 进行提交,M 进行合并) :
branch1 C2---C3---C6---C9---
/
master C0---C1---C4---C5---C7---C8
当前工作流程
可以定义当前使用的工作流程merge-up/merge-down:因为我不想修复master中的合并冲突,所以首先我merge- up master
inside branch1
, 然后我 merge-down branch1
in master
.
branch1 C2---C3---C6---C9---M1
/ /\
master C0---C1---C4---C5---C7---C8 M2
这样做,我解决了分支内部的冲突,然后我可以将我的分支合并到 master 中。
我个人不喜欢这个解决方案,主要原因有两个:
- 它导致一个非常混乱的历史树
- 将父分支合并到子分支对我来说听起来毫无意义
另一方面,我的同事认为:
- 它很容易理解,即使是非专业的实习同事也是如此
- 它会导致更少的错误,因为您没有触及历史记录(与 rebase 不同)
建议的解决方案
我提出的是常见且更直接的 rebase-before-merge-down 工作流程。
一旦我想在 master
中合并 branch1
,首先我 rebase 前者在后者上,所以我处理我分支中的所有冲突;比我 向下合并 branch1
到 master
(如果功能分支有意义,则使用 NO-FF,否则使用 FF)
branch1 C2---C3---C6
rebase / \
master C5---C7---C8 M1
This solution, however, has a main drawback:
Since both are synced with the remote, a
git push --force
is necessary. So, if someone do something wrong (because he's in a hurry, distracted or silly), weeks of work could be lost in one second.
另一方面,优点应该是:
- 保持历史树非常干净和有意义
- 展平并删除无用的分支,只保留相关的分支
所以,问题是?
您在大型团队中采用哪种可扩展的工作流程来保持 git 历史的清晰和有意义,另一方面,防止像错误 git push --force
那样的潜在灾难?
根据我的经验,一个常见的模型是这样的:
- 有一个权威的 master 存储库。
- 这里的
master
分支只能通过拉取请求更新(用你的话说,向下合并)。 - 是否可以强制推送到其他分支是临时决定的(往往取决于实际需要同时在一个分支上工作的人数)。
- 这里的
- 每个人都维护自己的主存储库分支。
- 他们可以随时强制推送到自己的分支。
- 要将更改合并到 master 存储库的
master
分支,您需要将分支变基到master
并拉入请求。
版本处理起来要棘手得多。
我建议查看 Linux、Git、Node 等开源项目如何维护其存储库并将其考虑在内。
如果branchX
(例如branch1
)适合你自己,你可以使用你想要的方式:合并前变基。
如果branchX
(比如branch1
)适用于所有开发者,你最好不要使用你的前置解决方案,你说的主要缺点是其他开发者会感到困惑,他们可能再也找不到自己的变化了。
有一种方法可以使用:git merge branch1 --squash
。
假设您 git 日志是:
branch1 C2---C3---C6---C9---
/
master C0---C1---C4---C5---C7---C8
执行 git merge branch1 --squash
后,git 日志将如下所示:
branch1 C2---C3---C6---C9
/
master C0---C1---C4---C5---C7---C8---M1
这使您的 master
分支看起来更清晰。如果你想开发新功能,你可以直接从 master
签出功能分支。
顺便说一句:我需要更正您在当前工作流程中使用的图表。
将 master
合并到 branch1
后,图形看起来像
C2---C3---C6---C9---M1 branch1
/ /
C0---C1---C4---C5---C7---C8 master
将branch1
合并到master
后,默认不会创建提交M2
,它只是一个快进合并。 branch1
和 master
都指向 M1
现在提交。
C2---C3---C6---C9---M1 branch1, master
/ /
C0---C1---C4---C5---C7---C8