ChatGPT解决这个技术问题 Extra ChatGPT

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

我在一个新项目上使用 Git,该项目有两个并行的——但目前是实验性的——开发分支:

大师:现有代码库的导入以及我通常确定的一些修改

exp1:实验分支#1

exp2:实验分支#2

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

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

我考虑过的方法:

git merge --no-commit 然后手动取消暂存大量我不想在分支之间通用的编辑。手动将公共文件复制到临时目录,然后 git checkout 移动到另一个分支,然后从临时目录中手动复制到工作树中。上面的一个变种。暂时放弃 exp 分支并使用两个额外的本地存储库进行实验。这使得手动复制文件更加简单。

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

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

1
12 revs, 10 users 28%

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

概括:

从要合并的分支中检查路径,$ git checkout source_branch -- ...

或选择性地合并大块头 $ git checkout -p source_branch -- ...

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

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

最后提交 $ git commit -m "'合并'这些更改"


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

您使用 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,如果您想将不同的更改拆分到单个文件(在该页面中搜索“split”),您可以只添加部分内容)。

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


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

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

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 无法自动合并文件,它会将该文件报告为“未合并”并生成一个副本,您需要在其中手动解决冲突。

为了进一步解释示例,假设您要将 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 的所有内容都被删除了。这包括整个文件和大块。你被警告了
j
jthill

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

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

这是替代方案:

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

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 等。还值得将这些行与 && 而不是换行符链接在一起。
不幸的是,答案中的链接似乎已失效。
不仅链接死了,而且还有 WOT 声誉不佳的警告。因此我删除了它。
C
Chronial

还有另一种方法:

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
在这个问题上发布了类似的答案:stackoverflow.com/a/11593308/47185
哦,我不知道结帐有补丁!我做了结帐/重置/添加 -p 代替。
确实是最简单的方法。 git checkout -p featurebranch 文件名。最好的事情是当命令运行时,它会给你 ay/n/e/?/...等。决定如何合并文件的选项。我尝试使用 e,我什至可以在应用之前编辑补丁......这太酷了。用于合并来自其他分支的选择性文件的真正单行器。
P
Peter Mortensen

虽然其中一些答案非常好,但我觉得没有人真正回答了 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 <path>/* 来获取文件组。
这工作正常,但添加了两个额外的提交对象。没什么大不了的,但有点乱
@MightyPork 你是对的。不幸的是,由于我很久以前写过这篇文章,我不再确定为什么“git stash”和“git merge stash”步骤在那里而不是“git commit”。
哦,这很清楚,我想。这样它会合并一个文件,不一定会覆盖目标分支上的先前更改。
C
Cory

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

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

使用您想要引入的更改创建(如果未创建)本地分支。 $ git branch mybranch 切换到它。 $ git checkout mybranch 从其他人的帐户中拉下您想要的更改。如果您还没有,您需要将它们添加为遥控器。 $ git remote add repos-w-changes 从他们的分支中拉下所有东西。 $ 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。)
谢谢,我刚刚删除了它,因为我现在不确定在哪里可以找到原件
m
mahemoff

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

git checkout master
git checkout feature1 Myclass.java

请注意,这将覆盖(而不是合并)并忽略主分支中的本地更改。


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

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

第一步:区分分支

git diff branch_b > 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 之外跟进,看看我们是否能弄清楚。
3
3 revs, 2 users 80%

以下是如何让历史记录跟踪来自另一个分支的几个文件,而无需大惊小怪,即使更“简单”的合并会带来更多您不想要的更改。

首先,您将采取不同寻常的步骤,提前声明您将要提交的是一个合并,而 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是一种更诚实的方式来做你想做的事情,在这里。如果您想对上次未带入的同一分支上的其他文件进行另一次“合并”,它将以“已经是最新的”消息阻止您。这是我们应该有的时候没有分支的症状,因为“来自”分支应该不止一个不同的分支。


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

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

# 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

我对此使用了轻微的变化。我没有合并,而是选择了樱桃。它完成了这项工作。这种方法的唯一缺点是您丢失了对原始提交哈希的引用。
P
Peter Mortensen

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

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/… 如此清晰、简洁且有效!
这将完全替换源分支中的文件内容,而不是合并
我正要这样回答,我以为我发明了尚未回答的新事物!但这是最简单的方法。这应该在上面!
J
JimStar

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

你可以轻松做到:

只需将此行添加到全局 .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>

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


P
Peter Mortensen

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

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

例子

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

git checkout branchB .gitignore

有关更多信息,请参阅帖子。


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

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

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

这是一些答案的混合。


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

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

来自上述链接的命令:

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

你测试过这个吗?我确信这些文件将从 替换,而不是被合并
P
Peter Mortensen

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

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


P
Peter Mortensen

我会做一个

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

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

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


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

您可以使用 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?


W
Wade

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

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

所以情况是你想要'feature'分支(分支点'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 好点,我很确定我现在找到了正确的。
P
Peter Mortensen

我编写了自己的脚本“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

P
Peter Mortensen

按文件选择性合并/提交的简单方法:

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 根据文档:“重置索引和工作树。自 以来对工作树中跟踪文件的任何更改都将被丢弃。”所以提交本身是安全的。
J
JBaczuk

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

<强> 1。临时复制分支
$ git checkout -b temp_branch

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

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

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


c
code4kix

如果您只需要合并特定目录并保持其他所有内容不变并保留历史记录,则可以尝试这样做...在实验之前从 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.

g
grenix

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

git difftool <branch-1>..<branch-2>

另见https://sites.google.com/site/icusite/setup/git-difftool


m
matt

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

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

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

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

$ ls
a   b   f

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

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

这是otherbranch的版本:

$ 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 的索引版本。但我们还没有完成! a 的 otherbranch 版本仍在工作树中:

$ 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

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


d
djanowski

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

如果该差异中有任何二进制文件,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

S
Shoniisra

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

总之:

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