进行git合并时是否可以排除特定的提交?


72

假设我想从发布分支合并到master分支,并且release分支中有一些我不想包含在master分支中的提交。有没有一种方法可以进行合并,以使这些提交中的一个或多个不会被合并?

到目前为止,我的策略是执行以下操作(以大师为主):

git merge --no-commit release-branch
# Resolve conflicts and apply reverse patch of the commits that I don't want included
git commit # Edit commit message so that it lists the commits that have been reverse-patched

有一个更好的方法吗?


git的可能重复-合并时跳过特定提交-我知道那是更年轻的,但它接受的答案更好
Tobias Kienzler

Answers:


47

创建一个新分支,以交互方式重新建立分支的基础,并删除不需要的提交,然后将其合并。

您不能在没有重新哈希的情况下将更改从分支的中间带走,但是当它在以后的合并中看到相同的更改时(例如,从摘樱桃和不知道什么),将发生正确的事情。


2
但是,当您尝试再次合并该分支时会发生什么?
Evgeny

4
用哪个确切的命令来完成?我看不到如何“丢弃”不需要的提交的方法
Henning

9
@Henning当您git rebase -i other-branch给它一个文本编辑器时,其中包含许多提交。删除不需要的行。
达斯汀

112

Pro Git书中找到了一个适合我的解决方案。

假设您要排除文件config.php

在分支A:

  1. .gitattributes使用以下行创建一个在同一目录中命名的文件:config.php merge=ours。这告诉git合并文件时使用什么策略。在这种情况下,它始终保留您的版本,即。您要合并到的分支上的版本。

  2. 添加.gitattributes文件并提交

在分支B上:重复步骤1-2

现在尝试合并。您的文件应保持不变。


1
对于将来的读者来说,这对于明确包含特定文件非常有用。(就我而言,我要将不同的分支部署到不同的服务器,并希望保持Capistrano部署脚本在每个分支上都是唯一的。)
charliepark 2011年

16
由于某种原因,这对我不起作用。我创建了一个新分支,并用提交更改了文件,并在两个分支中添加了.gitattributes文件。当我合并回原始分支时,似乎完全忽略了.gitattributes中的行,并且无论如何都拉入更改的文件。我缺少某些设置吗?
摇摆

1
对我不起作用。我也遵循VonC列出的问题中的说明。我在Windows上。我试图避免合并的文件是一个点文件(.core.config)。我需要用引号将其名称括起来吗?
肖恩

17
这仅在修改了两个文件的情况下有效,否则将忽略合并我们的文件
Caumons 2013年

3
@robbles Caumons是正确的。基本上,仅当存在合并冲突时才触发此属性。就您而言,您有一个FF合并。Git只是在不检查合并策略的情况下做到了
Johnny Z

18

如果您有一个支持分支,可以在其中修复错误并构建新版本。在master上,您具有下一个版本,在该版本中您还经常构建新版本。

每次构建新版本时,都会在某个文件中更改版本,提交该新文件,创建标签并推送。现在,从支持母版的合并将始终在包含版本信息的文件中发生冲突。

如果包含版本信息的文件包含版本信息,则可以使用fcurella的答案。但是,如果确实确实还包含可合并的信息(pom.xml,gradle.properties,MANIFEST.MF等),则必须执行一些额外的操作。

让我们使用以下示例

      C---D*---E---F* support
     /
A---B---G---H*---I master

其中带有星号的提交仅包含由于版本更改而引起的更改,在合并期间应忽略这些更改。

要将支持合并到主服务器中而不会由于版本构建而导致合并冲突,您可以执行以下任一操作:

多次合并提交

git checkout master
git merge C
git merge D -s ours
git merge E
git merge F -s ours

使用这个-s ours参数,我们告诉git仅记录合并而不更改工作区。这相当于svn--record-only选项

以上将导致以下布局

      -------------C---D*---E---F* support
     /              \   \    \   \
A---B---G---H*---I---J---K----L---M master

使用cherry-pick进行一次合并提交

git checkout master
git merge support -s ours --no-commit
git cherry-pick C E --no-commit
git commit -m 'merged support into master'

首先,我们开始合并,但仅记录我们正在合并,而不更改工作区,也无需执行合并提交。然后,我们正在挑选要合并的提交,再次没有提交。最后,我们提交合并。

以上将导致以下布局

      C---D*---E---F* support
     /              \
A---B---G---H*---I---J master

甚至可以使摘樱桃自动化。

git checkout master
git merge support -s ours --no-commit
for id in `git log support --reverse --not HEAD --format="%H [%an] %s" |
  grep -v "bump version" |
  sed "s/\(\w*\)\s.*/\1/g"`
do
  git cherry-pick --no-commit $id
done
git commit -m 'merged support into master'

有没有可以自动执行多个合并提交的示例?
桑托斯

可以使用Cherry Pick进行单个合并提交的解决方案真是太棒了!谢谢!这正是我所需要的。我们有两个来自同一来源的项目。我们需要在它们之间保持一些差异,但总的来说,我们需要来回合并大部分更改。很棒的帖子!
布伦特·

4

无法直接完成此操作的原因是,每个提交都包含指向父提交的链接(通常只有一个,但是对于合并,则有几个)。这样,如果您进行一次提交(按SHA1的总和),则整个历史记录也将固定不变,因为父级也包含指向其父级的链接,依此类推。因此,在历史记录中遗漏补丁的唯一方法是编写新补丁。git rebase -i在新创建的分支上可能是实现此目的的最简单方法。


2

也可以修改.git / info / attributes文件并将其保存在.git文件夹中,而不是整个都添加.gitattribute文件,因为这最终需要将它们添加到源代码管理中。


2

主要问题是:您如何表示要跳过的提交?

  1. 偷偷地把它们藏起来(不是我的最爱)
  2. 明确跳过它们
  3. 明确撤消它们

抱歉不行。2在历史图中无法表示。

1号是可能的,但我绝不会这样做:合并提交可以包含更改。–通常,合并提交指向另外两个分支合并的结果:在这些分支中开发的所有事物都应在合并后的代码中(即,在合并提交所指向的代码中)。此提交中没有其他内容。

但是令人惊讶的是,您可以更改整个代码库并将其表示为合并。合并的效果有两个:合并历史树并且应合并两个代码库。前者确实可以做到(否则没人会称其为合并),后者可能会失败,例如,当出现合并冲突并且错误解决了(然后代码库未正确合并)时。

其他一些答案表明这种隐藏。我建议使用显式方式:合并加还原提交。


对于短期分支机构来说,这是一个不错的策略。这种策略的问题在于,当您将正确合并的分支(带有还原的分支)合并回工作分支时,由于它是常规的真正提交,因此工作分支也将收到还原。如果您认为工作分支确实不应该包含提交,则必须还原还原。只要正在使用该特定工作分支,此过程就会继续。
罗恩·沃特伦

@RonWertlen正确的说法是,在不引入还原效果的情况下,母版不能再次合并到该“工作分支” /发行分支中。–但这不仅适用于我建议的答案策略3,还适用于策略1。–这也是我个人不愿意按照OP的问题进行工作流程的原因之一。
罗伯特·西默

我也同意你的看法 1和2是反工作流程。如果分支机构的生命周期很长,并且希望永久性地拒绝提交,那么现在是时候开始考虑重组项目并使用子模块了(对于node.js,您还可以将node_modules作为实现同一件事的并行机制)。
罗恩·沃特伦

此外,2.在历史图表中无法表达,该陈述不是100%清晰的。实际上,可以通过重置或重新建立基础并使用“ --force”来重写远程仓库的索引来“撒谎”自己的历史。这当然是一个非常糟糕的主意。
罗恩·沃特伦

@RonWertlen您刚刚提醒我一种可以合并某些内容但跳过某些提交的方法。–但这与遥控器无关。
罗伯特·西默

1

如果只想排除末尾的某些提交,则可以只提交给特定的提交编号:

git checkout partlyMergedFrom
git whatchanged
--> find the commit hash up to where you want to merge
git checkout partlyMergedInto
git merge e40a0e384f58409fe3c864c655a8d252b6422bfc
git whatchanged
--> check that you really got all the changes you want to have
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.