【问题标题】:Is it possible to exclude specific commits when doing a git merge?进行 git 合并时是否可以排除特定的提交?
【发布时间】:2010-09-24 20:31:41
【问题描述】:

假设我想从发布分支合并到主分支,并且发布分支中有一些我不想包含在主分支中的提交。有没有办法进行合并,这样一个或多个提交就不会被合并?

到目前为止,我的策略是执行以下操作(在 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


【解决方案1】:

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

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

很遗憾,没有。 2 在历史图中是不可能表达的。

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

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

其他一些答案暗示了这种隐藏。我推荐显式方式:merge plus revert commits。

【讨论】:

  • 这对于短期分支来说是一个很好的策略。这种策略的问题在于,当您将正确合并的分支(具有还原的分支)合并回工作分支时,工作分支也会收到还原,因为它是一个常规的、真正的提交。如果您遇到工作分支确实不应该有提交的情况,您将不得不还原还原。只要该特定工作分支正在使用中,这将继续。
  • @RonWertlen 你说得对,如果不引入还原的影响,master 就不能再次合并到那个“工作分支”/发布分支​​中。 – 但这不仅适用于我建议的答案策略 3,也适用于策略 1。 – 这也是我个人不会从 OP 的问题中追求工作流程的原因之一。
  • 我也同意你的看法。 1和2是反工作流程。如果您有长期存在的分支,并且希望永久保留一个提交和另一个提交,那么是时候开始考虑重组您的项目并使用子模块(对于 node.js,您还拥有 node_modules 作为实现相同目标的并行机制)。
  • 此外,没有 2. 在历史图中无法表达,此声明不是 100% 清楚的。实际上,可以通过使用“--force”重置或重新设置和重写远程仓库的索引来“欺骗”自己的历史。这当然是一个非常糟糕的主意。
  • @RonWertlen 你刚刚让我想起了一种合并某些东西但跳过一些提交的方法。 – 但这与遥控器无关。
【解决方案2】:

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

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

如果包含版本信息的文件包含版本信息,则可以选择 fcurella 的答案。但如果它确实也可能包含可合并信息(pom.xml、gradle.properties、MANIFEST.MF、...),则必须执行一些额外的操作。

让我们使用以下示例

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

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

要将 support 合并到 master 中而不会因版本构建而产生合并冲突,您可以执行以下任一操作:

多次合并提交

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

使用 -s ours 参数,我们告诉 git 只记录合并而不改变工作区。这相当于to the --record-only option of svn

以上将导致以下布局

      -------------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 提供单个合并提交的解决方案非常棒!谢谢!这正是我所需要的。我们有两个从同一来源分支的项目。我们需要保持它们之间的一些差异,但总的来说,我们需要来回合并大部分更改。很棒的帖子!
【解决方案3】:

我找到了适合我的解决方案in the Pro Git book

假设您要排除文件config.php

在分支 A:

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

  2. 添加.gitattributes文件并提交

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

立即尝试合并。您的文件应保持原样。

【讨论】:

  • 对于未来的读者来说,这非常适合明确包括特定文件。 (在我的例子中,我将不同的分支部署到不同的服务器,并希望在每个分支上保持我的 Capistrano 部署脚本唯一。)
  • 由于某种原因这对我不起作用。我创建了一个带有更改文件的提交的新分支,并在两个分支中添加了 .gitattributes 文件。当我合并回原始分支时,它似乎完全忽略了 .gitattributes 中的行,并且无论如何都会拉入更改的文件。有没有我遗漏的设置?
  • 对我不起作用。我也按照 VonC 列出的问题中的说明进行操作。我在 Windows 上。我试图避免合并的文件是一个点文件(.core.config)。我需要用引号或其他东西把它的名字括起来吗?
  • 这只有在两个文件被修改时才有效,否则合并我们的被忽略
  • @robbles Caumons 是对的。基本上,只有在存在合并冲突时才会触发此属性。在您的情况下,您有一个 FF 合并。 Git 只是在不检查合并策略的情况下执行此操作
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    如果你只想排除一些最后的提交,你可以只提交一个特定的提交号:

    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
    

    【讨论】:

      【解决方案6】:

      不能直接这样做的原因是每个提交都包含指向父提交的链接(通常只有一个,但有几个用于合并)。这样,如果你有一个提交(通过它的 SHA1 总和),整个历史也被固定,因为父母也包含到他们父母的链接等等。因此,在历史中遗漏补丁的唯一方法是编写一个新补丁。在新创建的分支上执行 git rebase -i 可能是实现这一目标的最简单方法。

      【讨论】:

        【解决方案7】:

        创建一个新分支,以交互方式重新设置分支并删除您不想要的提交,然后将其合并。

        如果不重新散列,您不能从分支中间取出更改,但是当它在以后的合并中看到相同的更改时(例如,来自樱桃采摘和其他什么),正确的事情就会发生。

        【讨论】:

        • 但是当您再次尝试合并该分支时会发生什么?
        • 这需要使用哪些确切的命令?我看不出如何“删除”不需要的提交
        • @Henning 当你 git rebase -i other-branch 它为你提供了一个包含大量提交的文本编辑器。删除不需要的行。
        猜你喜欢
        • 2020-02-15
        • 2018-10-17
        • 1970-01-01
        • 1970-01-01
        • 2010-10-18
        • 1970-01-01
        • 2016-07-06
        • 1970-01-01
        • 2016-04-23
        相关资源
        最近更新 更多