如何在 Git 的另一个分支中选择性地合并或选择更改?

Posted

技术标签:

【中文标题】如何在 Git 的另一个分支中选择性地合并或选择更改?【英文标题】:How can I selectively merge or pick changes from another branch in Git? 【发布时间】:2010-10-01 18:17:47 【问题描述】:

我在一个新项目中使用 Git,该项目有两个并行但目前处于试验阶段的开发分支:

master: 现有代码库的导入以及一些我通常确信的修改 exp1: 实验分支#1 exp2: 实验分支#2

exp1exp2 代表两种截然不同的架构方法。在我走得更远之前,我无法知道哪个(如果有的话)会起作用。当我在一个分支中取得进展时,我有时会进行一些对另一个分支有用的编辑,并且只想合并这些。

将选择性更改从一个开发分支合并到另一个开发分支的最佳方法是什么?

我考虑过的方法:

    git merge --no-commit 然后手动取消暂存大量我不想在分支之间通用的编辑。

    将常用文件手动复制到临时目录,然后 git checkout 移动到另一个分支,然后将更多手动文件从临时目录复制到工作树中。

    上面的一个变体。暂时放弃exp 分支并使用另外两个本地存储库进行实验。这使得手动复制文件更加简单。

所有这三种方法看起来都很乏味且容易出错。我希望有更好的方法;类似于过滤器路径参数的东西,可以使git-merge 更具选择性。

【问题讨论】:

如果您的实验分支中的更改在单独的提交中组织得很好,那么最好考虑合并选择性提交而不是选择性文件。下面的大多数答案都假设是这种情况。 git merge -s ours --no-commit 后跟一些 git read-tree 的组合不是一个很好的解决方案吗?见***.com/questions/1214906/… 最近的一个问题有一个写得很好的单行答案:***.com/questions/10784523/… 查看此博客以仅合并特定文件jasonrudolph.com/blog/2009/02/25/… 【参考方案1】:

我遇到了与您上面提到的完全相同的问题。但我发现this 在解释答案时更清楚。

总结:

从要合并的分支中检查路径,

 $ git checkout source_branch -- <paths>...

Hint: It also works without `--` like seen in the linked post.

或选择性地合并大块

 $ git checkout -p source_branch -- <paths>...

或者,使用 reset 然后添加选项 -p

    $ git reset <paths>...
    $ git add -p <paths>...

最后提交

 $ git commit -m "'Merge' these changes"

【讨论】:

Bart J 的链接文章是最好的方法。清晰,简单,一个命令。这是我将要使用的那个。 :) 这不是真正的合并。您正在按文件而不是按提交来选择更改,并且您将丢失任何现有的提交信息(作者、消息)。当然,如果您想合并某些文件中的所有更改并且您必须重新执行所有提交,这很好。但是,如果文件包含要合并的更改和要丢弃的其他更改,则其他答案中提供的一种方法会更好地为您服务。 @mykhal 和其他人:这会自动暂存索引中的文件,因此如果您签出foo.c,请执行git reset HEAD foo.c 取消暂存该文件,然后您可以对其进行比较。我在尝试后发现了这一点,然后回到这里寻找答案 查看您还可以使用的更改:git diff --cached 据此answer git checkout -p &lt;revision&gt; -- &lt;path&gt; 将与发出您描述的前三个命令相同:)【参考方案2】:

您使用cherry-pick 命令从一个分支获取单个提交。

如果您想要的更改不在单个提交中,请使用此处显示的方法split the commit into individual commits。粗略地说,您使用git rebase -i 来编辑原始提交,然后使用git reset HEAD^ 选择性地还原更改,然后使用git commit 将该位提交为历史中的新提交。

There is another nice method here 在 Red Hat Magazine 中,他们使用 git add --patch 或者可能是 git add --interactive,如果您想将不同的更改拆分到单个文件(在该页面中搜索“分裂”)。

拆分更改后,您现在可以只挑选您想要的。

【讨论】:

据我了解,这比投票率更高的答案更令人费解。 这在技术上是正确的答案,正确的答案确实显得“令人费解”。 --- 投票率较高的答案只是一个快速而肮脏的“解决问题”答案,对于大多数人来说,这就是他们所关心的(: @akaihola: HEAD^ 是正确的。请参阅 man git-rev-parse:修订参数的后缀 ^ 表示该提交对象的第一个父级。前缀 ^ 表示法用于排除可从提交中访问的提交。 我只是想分享另一种方法,它似乎是所有方法中最简洁且不那么复杂的:jasonrudolph.com/blog/2009/02/25/… 完全简单且很棒 对哪种方法“正确”的争论感到困惑?考虑the difference between files & commits (see Remarks at bottom)。 OP 想要合并 FILES 并且没有提到 COMMITS。投票率较高的答案是特定于文件的;接受的答案使用特定于提交的cherry-pick。 Cherry-pick 可能是选择性合并提交的关键,但将文件从一个分支移动到另一个分支可能会非常痛苦。尽管提交是 git 力量的核心,但不要忘记文件仍然有作用!【参考方案3】:

要选择性地将文件从一个分支合并到另一个分支,运行

git merge --no-ff --no-commit branchX

其中branchX 是您要合并到当前分支的分支。

--no-commit 选项将暂存已由 Git 合并的文件,而不实际提交它们。这将使您有机会根据需要修改合并的文件,然后自己提交它们。

根据你要如何合并文件,有四种情况:

1) 你想要一个真正的合并。

在这种情况下,您以 Git 自动合并文件的方式接受合并文件,然后提交它们。

2) 有些文件您不想合并。

例如,您希望保留当前分支中的版本并忽略要合并的分支中的版本。

要选择当前分支中的版本,运行:

git checkout HEAD file1

这将检索当前分支中file1 的版本并覆盖由Git 自动合并的file1

3) 如果您想要 branchX 中的版本(而不是真正的合并)。

运行:

git checkout branchX file1

这将在branchX 中检索file1 的版本,并覆盖由Git 自动合并的file1

4) 最后一种情况是,如果您只想选择 file1 中的特定合并。

在这种情况下,您可以直接编辑修改后的file1,将其更新为您希望file1 成为的任何版本,然后提交。

如果 Git 无法自动合并文件,它会将文件报告为“unmerged”并生成一个副本,您需要手动解决冲突。


为了进一步解释,假设您想将branchX 合并到当前分支中:

git merge --no-ff --no-commit branchX

然后运行git status 命令查看修改文件的状态。

例如:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

file1file2file3 是 git 已成功自动合并的文件。

这意味着所有这三个文件的masterbranchX 中的更改已合并在一起,没有任何冲突。

您可以通过运行git diff --cached 来检查合并是如何完成的;

git diff --cached file1
git diff --cached file2
git diff --cached file3

如果你发现一些不合需要的合并,那么你可以

    直接编辑文件 保存 git commit

如果不想合并file1,又想在当前分支中保留版本

运行

git checkout HEAD file1

如果你不想合并file2,只想要branchX中的版本

运行

git checkout branchX file2

如果您希望 file3 自动合并,请不要执行任何操作。

此时 Git 已经将其合并。

file4 以上是 Git 的失败合并。这意味着在同一行上发生的两个分支都有变化。这是您需要手动解决冲突的地方。您可以通过直接编辑文件或对您希望file4 成为的分支中的版本运行 checkout 命令来放弃合并完成的操作。

最后别忘了git commit

【讨论】:

小心:如果 git merge --no-commit branchX 只是一个快进,指针将被更新,因此 --no-commit 会被忽略 @cfi 添加--no-ff 来防止这种行为怎么样? 此解决方案提供最佳结果和灵活性。 与得票最多的答案不同,此解决方案保留了我的合并历史记录,这对我来说很重要,因为我在分支之间来回编织部分提交。我没有尝试所有其他建议的解决方案,所以也许其中一些也这样做了。 非常小心。此合并在一个方向上起作用,但如果您决定将上游 master 合并回源分支,则未包含在合并中的文件将被视为已删除。我使用一个类似于 git-flow 的过程,它使用一个主分支(生产主线)、一个暂存分支(暂存服务器主线)和基于暂存分支的主题分支。使用这种策略导致我从 master 到 staging 的“反向合并”完全失败,认为我没有从 staging 合并到 master 的所有内容都被删除了。这包括整个文件和大块。你被警告了【参考方案4】:

我不喜欢上述方法。使用cherry-pick 非常适合选择单个更改,但是如果您想要引入所有更改(除了一些不好的更改),那就很痛苦了。这是我的方法。

没有--interactive 参数可以传递给 git merge。

这里是替代方案:

您在分支“功能”中有一些更改,并且您想以一种不马虎的方式将其中一些但不是全部带到“主控”(即您不想挑选并提交每一个)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

所以只需将其包装在一个 shell 脚本中,将 master 更改为 $to 并将 feature 更改为 $from 就可以了:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b $from_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . $from_tmp
git branch -d $from_tmp

【讨论】:

我修正了格式 - 这是一个非常好的方法,如果你想选择提交 我现在正在使用这种技术,它似乎效果很好。 您可能希望将git rebase -i $to 更改为git rebase -i $to || $SHELL,以便用户可以在rebase 失败时根据需要调用git --skip 等。也值得将这些行与&amp;&amp; 链接在一起而不是换行符。 不幸的是,答案中的链接似乎已失效。 不仅链接失效,而且还有 WOT 信誉不佳的警告。因此我删除了它。【参考方案5】:

还有一条路可以走:

git checkout -p

它是git checkoutgit add -p 的混合体,可能正是您正在寻找的:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.

【讨论】:

这是迄今为止最简单、最简单的方法,只要您只有可管理数量的更改要合并。我希望更多的人会注意到这个答案并点赞。示例:git checkout --patch exp1 file_to_merge 在这个问题上发布了类似的答案:***.com/a/11593308/47185 哦,我不知道结帐有补丁!我做了 checkout/reset/add -p 代替。 真正最简单的方法。 git checkout -p featurebranch 文件名。最好的事情是当命令运行时,它会给你一个 y/n/e/?/...等。决定如何合并文件的选项。我尝试使用 e,我什至可以在应用之前编辑补丁......这太酷了。用于合并来自其他分支的选择性文件的真正单行。【参考方案6】:

虽然其中一些答案非常好,但我觉得没有人真正回答了 OP 的原始约束:从特定分支中选择特定文件。这个解决方案可以做到这一点,但如果文件很多,它可能会很乏味。

假设您有 masterexp1exp2 分支。您想将每个实验分支中的一个文件合并到 master 中。我会这样做:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# Save these files as a stash
git stash

# Merge stash with master
git merge stash

这将为您提供所需的每个文件的文件内差异。而已。一点也不差。在版本之间进行完全不同的文件更改很有用——在我的例子中,将应用程序从 Ruby on Rails 2 更改为 Ruby on Rails 3。

这将合并文件,但它会进行智能合并。我无法弄清楚如何使用这种方法来获取文件内差异信息(也许它仍然会导致极端差异。除非你使用 -s recursive -X ignore-all-space 选项,否则像空格这样烦人的小东西会被合并回来)

【讨论】:

另外注意:你可以从一个给定的分支做多个文件都内联,例如git checkout exp1 path/to/file_a path/to/file_x 这很漂亮。我做了git checkout feature &lt;path&gt;/* 来获取文件组。 这工作正常,但添加了两个额外的提交对象。没什么大不了的,但有点乱 @MightyPork 你是对的。不幸的是,由于我很久以前写过这篇文章,我不再确定为什么“git stash”和“git merge stash”步骤在那里而不是“git commit”。 哦,这很清楚,我想。这样它会合并一个文件,而不必覆盖目标分支上以前的更改。【参考方案7】:

1800 INFORMATION's answer 完全正确。但是,作为 Git 的新手,“使用 git cherry-pick”不足以让我在没有更多的互联网挖掘的情况下弄清楚这一点,所以我想我会发布一个更详细的指南,以防其他人在类似的船上。

我的用例是希望有选择地将其他人的 GitHub 分支中的更改提取到我自己的分支中。如果您已经有一个包含更改的本地分支,则只需执行步骤 2 和 5-7。

    创建(如果未创建)带有您想要引入的更改的本地分支。

    $ git branch mybranch &lt;base branch&gt;

    切换到它。

    $ git checkout mybranch

    从其他人的帐户中下拉您想要的更改。如果您还没有,您需要将它们添加为遥控器。

    $ git remote add repos-w-changes &lt;git url&gt;

    从他们的分支中拉下所有东西。

    $ git pull repos-w-changes branch-i-want

    查看提交日志以查看您想要的更改:

    $ git log

    切换回您想要将更改拉入的分支。

    $ git checkout originalbranch

    Cherry 用哈希值一一挑选你的提交。

    $ git cherry-pick -x hash-of-commit

【讨论】:

提示:首先使用git cherry 命令(先看手册)来识别尚未合并的提交。 这可行.. 1. 创建了一个新分支 2. 创建了一些文件/进行了一些更改 3. 提交 4. 签出主分支 5. 运行 git cherry-pick -x hash-of-commit并解决合并冲突,你很高兴。 您的链接已失效。请问可以更新一下吗? 链接(实际上)已损坏:“需要身份验证...https://www.sourcemage.org 正在请求您的用户名和密码。网站显示:“受限区域””。 (它也重定向到 HTTPS。) 谢谢,我刚刚删除了它,因为我现在不确定在哪里可以找到原件【参考方案8】:

以下是如何将master 分支中的Myclass.java 文件替换为feature1 分支中的Myclass.java。即使Myclass.javamaster 上不存在,它也会起作用。

git checkout master
git checkout feature1 Myclass.java

请注意,这将覆盖 - 不合并 - 而忽略 master 分支中的本地更改。

【讨论】:

这不会合并。它只会用 feature1 分支的更改覆盖 master 上的更改。 完美,我一直在寻找这种合并 theirs 覆盖 ours => +1 Cheers ;) 有时您只想替换整个文件,这就是我想要的,但您需要确保您希望丢失您对此文件所做的所有更改。 最干净的解决方案,因为 OP 特别想用另一个分支上的等效文件替换整个文件:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.【参考方案9】:

简单的方法,实际上合并来自两个分支的特定文件,而不仅仅是用来自另一个分支的文件替换特定文件。

第一步:区分分支

git diff branch_b &gt; my_patch_file.patch

创建当前分支和branch_b之间差异的补丁文件

第二步:对匹配模式的文件应用补丁

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

关于选项的有用说明

您可以在包含模式中使用* 作为通配符。

斜线不需要转义。

另外,您可以使用 --exclude 代替并将其应用于除与模式匹配的文件之外的所有内容,或者使用 -R 反转补丁

-p1 选项是 *Unix 补丁命令的保留,补丁文件的内容在每个文件名前加上 a/b/(或更多取决于补丁文件的生成方式)需要剥离,这样才能找出真正的文件到需要应用补丁的文件的路径。

查看 git-apply 的手册页了解更多选项。

第三步:没有第三步

显然你想提交你的更改,但谁说你在提交之前没有其他相关的调整。

【讨论】:

这在 current_branch 有许多需要保留的“附加”更改时非常有用。得到了由 branch_b 带来的唯一变化的差异: git diff HEAD...branch_b (是的——三个句点就是魔术)。 @masukomi,在第 2 步中,您不应该将第 1 步中创建的补丁文件添加为参数吗? 对我来说,所有更改都被拒绝。知道为什么吗? 最初的想法是@LinusGeffarth 可能是你在制作补丁时把分支弄反了?将在 SO 之外跟进,看看我们是否能弄清楚。【参考方案10】:

您可以通过以下方法轻松获取历史记录,以跟踪来自另一个分支的几个文件,即使更“简单”的合并会带来更多您不想要的更改。

首先,您将采取不同寻常的步骤,提前声明您将要提交的是一个合并,而不需要 Git 对您工作目录中的文件做任何事情:

git merge --no-ff --no-commit -s ours branchname1

...其中“分支名称”是您声称要合并的任何内容。如果您要立即提交,它不会做任何更改,但它仍会显示来自另一个分支的祖先。如果需要,您也可以在命令行中添加更多分支、标签等。不过,此时没有要提交的更改,所以接下来从其他修订版中获取文件。

git checkout branchname1 -- file1 file2 etc.

如果您从多个其他分支合并,请根据需要重复。

git checkout branchname2 -- file3 file4 etc.

现在来自另一个分支的文件在索引中,准备好提交,带有历史记录。

git commit

您将在该提交消息中进行大量解释。

但请注意,如果不清楚,这是一件搞砸的事情。它不符合“分支”的精神,而cherry-pick是一种更诚实的方式来做你正在做的事情,在这里。如果您想对上次未带入的同一分支上的其他文件进行另一次“合并”,它将以“已经是最新的”消息阻止您。这是我们应该有的时候没有分支的症状,因为“from”分支应该不止一个不同的分支。

【讨论】:

您的第一个命令 (git merge --no-ff --no-commit -s outs branchname1) 正是我想要的!谢谢! 有多个分支,需要历史,需要合并单个文件并且必须在推送之前更改文件的内容,这似乎是一个不错的选择。例如 dev => master,但您想在推送到 master 之前更改主机定义或类似内容。 这非常有效,我所做的唯一更改是使用git merge --continue 而不是最终的git commit。谢谢!【参考方案11】:

这是我合并选择性文件的工作流程。

# Make a new branch (this will be temporary)
git checkout -b newbranch

# Grab the changes
git merge --no-commit  featurebranch

# Unstage those changes
git reset HEAD
(You can now see the files from the merge are unstaged)

# Now you can chose which files are to be merged.
git add -p

# Remember to "git add" any new files you wish to keep
git commit

【讨论】:

我对此做了些许改动。我没有合并,而是选择了樱桃。它完成了这项工作。这种方法的唯一缺点是您丢失了对原始提交哈希的引用。【参考方案12】:

最简单的方法是将你的仓库设置为你要合并的分支,然后运行

git checkout [branch with file] [path to file you would like to merge]

如果你跑了

git status

你会看到文件已经暂存...

然后运行

git commit -m "Merge changes on '[branch]' to [file]"

简单。

【讨论】:

这几乎是我找到的最佳答案。请参阅jasonrudolph.com/blog/2009/02/25/… 如此清晰、简洁,而且效果很好! 这将完全替换源分支中的文件内容而不是合并 我正要这样回答,我以为我发明了尚未回答的新事物!但这是最简单的方法。这应该在上面!【参考方案13】:

奇怪的是,git 仍然没有“开箱即用”这么方便的工具。在更新一些旧版本分支(仍然有很多软件用户)时,我大量使用它,仅通过当前版本分支中的 一些 错误修复。在这种情况下,通常需要从主干文件中快速获取仅一些行代码,而忽略许多其他更改(不应该进入旧版本)......并且当然,在这种情况下需要交互式三路合并,git checkout --patch &lt;branch&gt; &lt;file path&gt; 不适用于这种选择性合并目的。

您可以轻松做到:

只需将此行添加到全局.gitconfig 或本地.git/config 文件中的[alias] 部分:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

这意味着您使用 Beyond Compare。如果需要,只需更改为您选择的软件。或者如果您不需要交互式选择性合并,您可以将其更改为三向自动合并:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

然后像这样使用:

git mergetool-file <source branch> <file path>

这将为您提供真正的选择性tree-way合并其他分支中任何文件的机会。

【讨论】:

【参考方案14】:

我发现this post 包含最简单的答案。只做:

git checkout <branch from which you want files> <file paths>

示例

将 .gitignore 文件从 branchB 拉入当前分支:

git checkout branchB .gitignore

查看帖子了解更多信息。

【讨论】:

这并没有真正合并,它覆盖了当前分支上的文件。 @igrali 这是一个有用的评论,但与“正确”方法的难度相比,这是一个很好的解决方法。一个人必须非常小心。【参考方案15】:

这不是您想要的,但它对我很有用:

git checkout -p <branch> -- <paths> ...

这是一些答案的混合。

【讨论】:

这确实很有用,可以添加到对我来说是最好的答案,@alvinabad 的答案。执行时:git checkout HEAD file1 保留当前版本并取消合并文件file1,可以使用-p 选项选择要合并的文件的部分。谢谢你的伎俩! 这是做什么的?您的答案中的 cmets 无法解释此命令的行为方式。【参考方案16】:

我遇到了与您上面提到的完全相同的问题。但我发现this Git blog 在解释答案时更清楚。

来自上述链接的命令:

# You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>

【讨论】:

你测试过这个吗?我确信文件将从 替换,而不是被合并【参考方案17】:

我会做一个

git diff commit1..commit2 文件模式 | git-apply --index && git commit

通过这种方式,您可以限制来自分支的文件模式的提交范围。

它是从 Re: How to pull only a few files from one branch to another?

偷来的

【讨论】:

在某些情况下,这可能非常方便。但是,如果更改位于不同的分支中,您可以从该分支的尖端签出,如上面 Bart J 的回答。 巴特·杰斯是谁? 这与下面的git checkout branch ... 相同 - 删除了branch 中不存在的本地更改(!)。它们在补丁文件中被标记为“已删除”,因为普通 diff 不会检查提交历史记录。【参考方案18】:

对我来说,git reset --soft branch 是从另一个分支中选择性地选择更改的最简单方法,因为此命令会将所有差异更改放入我的工作树中,我可以轻松地选择或恢复我需要的那个。

通过这种方式,我可以完全控制提交的文件。

【讨论】:

【参考方案19】:

我喜欢previous 'git-interactive-merge' 的答案,但有一个更简单的答案。让 Git 使用交互式和 on 的变基组合为您完成此操作:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

所以情况是您想要来自“功能”分支(分支点“A”)的 C1 和 C2,但现在不需要其余的。

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

与上一个答案一样,它会将您带到交互式编辑器中,您可以在其中为 C1 和 C2 选择“选择”行(如上)。保存并退出,然后它会继续 rebase 并在 master + C1 + C2 处为您提供分支 'temp' 和 HEAD:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

然后您可以将 master 更新为 HEAD 并删除 temp 分支,您就可以开始了:

# git branch -f master HEAD
# git branch -d temp

【讨论】:

您能否将直接链接到您所指的其他答案(用户名不够稳定,因为它们可以随时更改)? (按“最旧”排序以限制可能答案的数量(在您之前)。) @PeterMortensen 好点子,我很确定我现在找对了。【参考方案20】:

我编写了自己的脚本“pmerge”来部分合并目录。这是一项正在进行的工作,我仍在学习 Git 和 Bash 脚本。

此命令使用git merge --no-commit,然后取消应用与提供的路径不匹配的更改。

用法:git pmerge branch path 示例:git merge develop src/

我没有对它进行广泛的测试。工作目录应该没有任何未提交的更改和未跟踪的文件。

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'

# List of changes due to merge | replace nulls with newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\1,\//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # Reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS

【讨论】:

【参考方案21】:

您可以使用read-tree 将给定的远程树读取或合并到当前索引中,例如:

git remote add foo git@example.com/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

要执行合并,请改用-m

另请参阅:How do I merge a sub directory in Git?

【讨论】:

【参考方案22】:

一种通过文件选择性合并/提交的简单方法:

git checkout dstBranch
git merge srcBranch

// Make changes, including resolving conflicts to single files
git add singleFile1 singleFile2
git commit -m "message specific to a few files"
git reset --hard # Blow away uncommitted changes

【讨论】:

git reset --hard 不会删除已提交的更改吗? @Prometheus 根据文档:“重置索引和工作树。自 以来对工作树中跟踪文件的任何更改都将被丢弃。”所以提交本身是安全的。【参考方案23】:

如果您没有太多已更改的文件,这将使您没有额外的提交。

1.暂时复制分支$ git checkout -b temp_branch

2。重置为上次想要的提交$ git reset --hard HEAD~n,其中n 是您需要返回的提交次数

3.从原始分支签出每个文件$ git checkout origin/original_branch filename.ext

如果需要,现在您可以提交和强制推送(覆盖远程)。

【讨论】:

【参考方案24】:

如果您只需要合并一个特定目录并保持其他所有内容不变并保留历史记录,您可以尝试这个...在您实验之前从master 创建一个新的target-branch

以下步骤假设您有两个分支target-branchsource-branch,并且您要合并的目录dir-to-mergesource-branch 中。还假设您在目标中有其他目录,例如 dir-to-retain,您不想更改和保留历史记录。此外,假设dir-to-merge 中存在合并冲突。

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.

【讨论】:

【参考方案25】:

当两个分支的当前提交之间只有少数文件发生更改时,我通过查看不同的文件手动合并更改。

git difftool &lt;branch-1&gt;..&lt;branch-2&gt;

另请参阅 https://sites.google.com/site/icusite/setup/git-difftool

【讨论】:

【参考方案26】:

我将专注于我感兴趣的这个问题的子集:我有两个分支,我想将 一个文件从一个伪合并到另一个.

(我说“伪合并”是因为我不需要或不想要合并提交;我只想以我认为合适的方式组合文件的两个版本的贡献。)

我的方法是基于https://***.com/a/39916536/341994 中采用的方法。不幸的是,该问题已作为重复项关闭(错误地,在我看来:它不是该问题的重复项,并且回答者在那里所做的回答和关闭重复项是错误的)。但是这个答案有一些问题,所以我对方法进行了现代化和清理。我使用restore 代替checkoutreset,而且我不会费心去提交我不需要的任何事情。

好的,想象一下我有三个文件:

$ ls
a   b   f

但我只想从otherbranch 伪合并其中一个a。让我们看看他们,看看情况会是什么样子。这是我的版本:

$ cat a
line one
line two
line three
line four
line five

这是另一个分支的版本:

$ git show otherbranch:a
line one
line two edited
line three
line four
line five
line six

现在这里的诀窍是我们将使用索引作为便签本(毕竟,这就是它的用途)。因此,我们从确保将我们的版本复制到索引中开始(步骤 1):

$ git add a

现在(第 2 步)我们可以使用 restoreotherbranch 获取版本(现在,restorecheckout 更好,因为它可以让我们说得更清楚):

$ git restore --source otherbranch a

乍一看,这看起来很糟糕。我们现在已经用otherbranch 的版本完全覆盖了我们的 a,如您所见:

$ cat a
line one
line two edited
line three
line four
line five
line six

但不用担心!如您所见,之前版本的 a 仍在索引中:

$ git diff a
diff --git a/a b/a
index abf51fa..333614b 100644
--- a/a
+++ b/a
@@ -1,6 +1,7 @@
 line one
-line two
+line two edited
 line three
 line four
 line five
+line six

很好,现在我们已准备好进行关键步骤(第 3 步)。我们对我们的文件从工作树到索引进行交互式补丁add

我们可以说git add -p a 来启动交互式补丁过程,在这种情况下,我们一次只吃一大块。但在这种情况下,只有一个大块,我还是想编辑它,所以我说:

$ git add --e a

结果是我们在编辑器中打开了一个差异补丁文件!它看起来像这样:

 line one
-line two
+line two edited
 line three
 line four
 line five
+line six

通过仔细编辑,我们现在可以决定我们想要接受哪些部分以及不接受哪些部分。让我们接受“第六行”而不是“第二行已编辑”。所以我们编辑成这样:

 line one
 line two
 line three
 line four
 line five
+line six

我们关闭编辑器并将补丁应用于 a 的索引版本。但我们还没有完成! otherbranch 版本的 a 仍在工作树中:

$ cat a
line one
line two edited
line three
line four
line five
line six

我们喜欢的版本在索引中,记得吗?为了得到它,(第 4 步)我们只需调用 git restore 简单明了(同样,这是现代方式;restorereset 更好,并且可以应用于单个文件):

$ git restore a

现在我们的 a 是正确的,我们都完成了:

$ cat a
line one
line two
line three
line four
line five
line six

此时我们可以提交,但我们不必这样做;我们已经完成了我们打算完成的事情。

【讨论】:

【参考方案27】:

我想要什么:交互式地从一个分支(有几个混乱的提交)中挑选大块到一个新分支中的一个干净的提交中。

git diff + git apply 如果您在该差异中有任何二进制文件,则将不起作用。

我的做法:

# New branch from a clean starting point, e.g. master
git checkout new-clean-branch origin/master

# Get all changes from the messy branch
# (quote the star so your shell doesn't expand it)
git checkout messy-branch -- '*'

# Unstage ("un-add") everything
git restore --staged .

# Interactively add hunks to be staged for commit
git add -p

【讨论】:

【参考方案28】:

如果您是 Gitkraken 用户here you have a small guide

总结:

    移动到要进行更改的分支。 (例如开发) 右键单击有新更改的分支并选择“cherrypick commit”选项(例如,功能-ABC)。 最后接受并检查是否存在冲突。

【讨论】:

以上是关于如何在 Git 的另一个分支中选择性地合并或选择更改?的主要内容,如果未能解决你的问题,请参考以下文章

Git合并指定文件到另一个分支

Git合并指定文件到另一个分支

eclipse git如何切换分支,合并分支,解决冲突等

eclipse git如何切换分支,合并分支,解决冲突等

强制 Git 在合并期间始终选择特定文件的较新版本?还是只提交特定的分支?

Git分支合并选择