如何有选择地合并或挑选来自 Git 中另一个分支的更改?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/449541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to selectively merge or pick changes from another branch in Git?
提问by David Joyner
I'm using git on a new project that has two parallel -- but currently experimental -- development branches:
我在一个新项目中使用 git,该项目有两个并行的——但目前处于实验阶段——开发分支:
master
: import of existing codebase plus a few mods that I'm generally sure ofexp1
: experimental branch #1exp2
: experimental branch #2
master
:导入现有的代码库以及一些我通常确定的模块exp1
:实验分支#1exp2
:实验分支#2
exp1
and exp2
represent two very different architectural approaches. Until I get further along I have no way of knowing which one (if either) will work. As I make progress in one branch I sometimes have edits that would be useful in the other branch and would like to merge just those.
exp1
并exp2
代表两种截然不同的架构方法。在我进一步相处之前,我无法知道哪一个(如果有的话)会起作用。当我在一个分支中取得进展时,我有时会在另一个分支中进行有用的编辑,并且只想合并那些。
What is the best way to merge selective changes from one development branch to another while leaving behind everything else?
将选择性更改从一个开发分支合并到另一个开发分支的最佳方法是什么?
Approaches I've considered:
我考虑过的方法:
git merge --no-commit
followed by manual unstaging of a large number of edits that I don't want to make common between the branches.Manual copying of common files into a temp directory followed by
git checkout
to move to the other branch and then more manual copying out of the temp directory into the working tree.A variation on the above. Abandon the
exp
branches for now and use two additional local repositories for experimentation. This makes the manual copying of files much more straightforward.
git merge --no-commit
然后手动取消大量编辑,我不想在分支之间通用。将公共文件手动复制到临时目录,然后
git checkout
移动到另一个分支,然后从临时目录手动复制到工作树中。上面的一个变体。暂时放弃
exp
分支并使用两个额外的本地存储库进行实验。这使得手动复制文件变得更加简单。
All three of these approaches seem tedious and error-prone. I'm hoping there is a better approach; something akin to a filter path parameter that would make git-merge
more selective.
所有这三种方法都显得乏味且容易出错。我希望有更好的方法;类似于过滤器路径参数的东西,可以使git-merge
更具选择性。
采纳答案by 1800 INFORMATION
You use the cherry-pickcommand to get individual commits from one branch.
您可以使用cherry-pick命令从一个分支获取单个提交。
If the change(s) you want are not in individual commits, then use the method shown here to split the commit into individual commits. Roughly speaking, you use git rebase -i
to get the original commit to edit, then git reset HEAD^
to selectively revert changes, then git commit
to commit that bit as a new commit in the history.
如果您想要的更改不在单独的提交中,则使用此处显示的方法将提交拆分为单独的提交。粗略地说,您习惯于git rebase -i
让原始提交进行编辑,然后git reset HEAD^
有选择地还原更改,然后git commit
将该位作为历史记录中的新提交进行提交。
There is another nice method herein Red Hat Magazine, where they use git add --patch
or possibly git add --interactive
which allows you to add just parts of a hunk, if you want to split different changes to an individual file (search in that page for "split").
在 Red Hat Magazine 中还有另一种不错的方法,如果您想将不同的更改拆分到单个文件(在该页面中搜索“拆分”),他们使用git add --patch
或可能git add --interactive
允许您仅添加大块的一部分。
Having split the changes, you can now cherry-pick just the ones you want.
拆分更改后,您现在可以只挑选您想要的更改。
回答by Susheel Javadi
I had the exact same problem as mentioned by you above. But I found thisclearer in explaining the answer.
我遇到了与您上面提到的完全相同的问题。但我在解释答案时发现这一点更清楚。
Summary:
概括:
Checkout the path(s) from the branch you want to merge,
$ git checkout source_branch -- <paths>...
Hint: It also works without
--
like seen in the linked post.or to selectively merge hunks
$ git checkout -p source_branch -- <paths>...
Alternatively, use reset and then add with the option
-p
,$ git reset <paths>... $ git add -p <paths>...
Finally commit
$ git commit -m "'Merge' these changes"
从要合并的分支中检出路径,
$ git checkout source_branch -- <paths>...
提示:它也可以在没有
--
链接的帖子中看到的情况下工作。或者有选择地合并帅哥
$ git checkout -p source_branch -- <paths>...
或者,使用重置然后添加选项
-p
,$ git reset <paths>... $ git add -p <paths>...
最后提交
$ git commit -m "'Merge' these changes"
回答by alvinabad
To selectively merge files from one branch into another branch, run
要将文件从一个分支选择性地合并到另一个分支,请运行
git merge --no-ff --no-commit branchX
where branchX
is the branch you want to merge from into the current branch.
branchX
您要合并到当前分支的分支在哪里。
The --no-commit
option will stage the files that have been merged by Git without actually committing them. This will give you the opportunity to modify the merged files however you want to and then commit them yourself.
该--no-commit
选项将暂存已被 Git 合并的文件,而无需实际提交它们。这将使您有机会根据需要修改合并的文件,然后自己提交。
Depending on how you want to merge files, there are four cases:
根据您要如何合并文件,有四种情况:
1) You want a true merge.
1)您想要真正的合并。
In this case, you accept the merged files the way Git merged them automatically and then commit them.
在这种情况下,您以 Git 自动合并文件的方式接受合并文件,然后提交它们。
2) There are some files you don't want to merge.
2) 有些文件您不想合并。
For example, you want to retain the version in the current branch and ignore the version in the branch you are merging from.
例如,您希望保留当前分支中的版本并忽略您正在合并的分支中的版本。
To select the version in the current branch, run:
要选择当前分支中的版本,请运行:
git checkout HEAD file1
This will retrieve the version of file1
in the current branch and overwrite the file1
automerged by Git.
这将检索file1
当前分支中的版本并覆盖file1
由 Git 自动合并的版本。
3) If you want the version in branchX (and not a true merge).
3)如果您想要 branchX 中的版本(而不是真正的合并)。
Run:
跑:
git checkout branchX file1
This will retrieve the version of file1
in branchX
and overwrite file1
auto-merged by Git.
这将检索由 Git 自动合并的file1
inbranchX
和覆盖版本file1
。
4) The last case is if you want to select only specific merges in file1
.
4)最后一种情况是,如果您只想在file1
.
In this case, you can edit the modified file1
directly, update it to whatever you'd want the version of file1
to become, and then commit.
在这种情况下,您可以file1
直接编辑修改后的内容,将其更新为您希望版本file1
成为的任何内容,然后提交。
If Git cannot merge a file automatically, it will report the file as "unmerged" and produce a copy where you will need to resolve the conflicts manually.
如果 Git 无法自动合并文件,它会将文件报告为“未合并”并生成一个副本,您需要在其中手动解决冲突。
To explain further with an example, let's say you want to merge branchX
into the current branch:
为了通过示例进一步解释,假设您要合并branchX
到当前分支:
git merge --no-ff --no-commit branchX
You then run the git status
command to view the status of modified files.
然后运行该git status
命令以查看已修改文件的状态。
For example:
例如:
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
#
Where file1
, file2
, and file3
are the files git have successfully auto-merged.
其中file1
、file2
和file3
是 git 已成功自动合并的文件。
What this means is that changes in the master
and branchX
for all those three files have been combined together without any conflicts.
这意味着,在这变化master
,并branchX
为所有这三个文件已经被结合在一起,没有任何冲突。
You can inspect how the merge was done by running the git diff --cached
;
您可以通过运行来检查合并是如何完成的git diff --cached
;
git diff --cached file1
git diff --cached file2
git diff --cached file3
If you find some merge undesirable then you can
如果您发现某些合并不受欢迎,那么您可以
- edit the file directly
- save
git commit
- 直接编辑文件
- 节省
git commit
If you don't want to merge file1
and want to retain the version in the current branch
如果不想合并file1
,想保留当前分支中的版本
Run
跑
git checkout HEAD file1
If you don't want to merge file2
and only want the version in branchX
如果您不想合并file2
并且只想要版本branchX
Run
跑
git checkout branchX file2
If you want file3
to be merged automatically, don't do anything.
如果你想file3
自动合并,什么都不做。
Git has already merged it at this point.
此时 Git 已经合并了它。
file4
above is a failed merge by Git. This means there are changes in both branches that occur on the same line. This is where you will need to resolve the conflicts manually. You can discard the merged done by editing the file directly or running the checkout command for the version in the branch you want file4
to become.
file4
上面是 Git 失败的合并。这意味着在同一行上发生的两个分支中都有变化。这是您需要手动解决冲突的地方。您可以通过直接编辑文件或对要file4
成为的分支中的版本运行 checkout 命令来丢弃已完成的合并。
Finally, don't forget to git commit
.
最后,别忘了git commit
。
回答by nosatalian
I don't like the above approaches. Using cherry-pick is great for picking a single change, but it is a pain if you want to bring in all the changes except for some bad ones. Here is my approach.
我不喜欢上述方法。使用cherry-pick 非常适合挑选单个更改,但是如果您想引入所有更改,除了一些不好的更改,那就很痛苦了。这是我的方法。
There is no --interactive
argument you can pass to git merge.
没有任何--interactive
参数可以传递给 git merge。
Here is the alternative:
这是替代方案:
You have some changes in branch 'feature' and you want to bring some but not all of them over to 'master' in a not sloppy way (i.e. you don't want to cherry pick and commit each one)
您在分支“功能”中有一些更改,并且您想以一种不马虎的方式将其中一些但不是全部带到“主”(即您不想挑选并提交每个)
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
So just wrap that in a shell script, change master into $to and change feature into $from and you are good to go:
因此,只需将其包装在 shell 脚本中,将 master 更改为 $to 并将 feature 更改为 $from,您就可以开始了:
#!/bin/bash
# git-interactive-merge
from=
to=
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
回答by Chronial
There is another way do go:
还有一种方法可以走:
git checkout -p
It is a mix between git checkout
and git add -p
and might quite be exactly what you are looking for:
这是之间的混合git checkout
,并git 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.
回答by Eric Hu
While some of these answers are pretty good, I feel like none actually answered OP's original constraint: selecting particular files from particular branches. This solution does that, but may be tedious if there are many files.
虽然其中一些答案非常好,但我觉得没有一个真正回答了 OP 的原始约束:从特定分支中选择特定文件。这个解决方案可以做到这一点,但如果有很多文件,可能会很乏味。
Lets say you have the master
, exp1
, and exp2
branches. You want to merge one file from each of the experimental branches into master. I would do something like this:
比方说你有master
,exp1
和exp2
分支机构。您想将每个实验分支中的一个文件合并到 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
This will give you in-file diffs for each of the files you want. Nothing more. Nothing less. It's useful you have radically different file changes between versions--in my case, changing an app from Rails 2 to Rails 3.
这将为您提供所需的每个文件的文件内差异。而已。一点也不少。在版本之间进行完全不同的文件更改很有用 - 在我的情况下,将应用程序从 Rails 2 更改为 Rails 3。
EDIT: this will merge files, but does a smart merge. I wasn't able to figure out how to use this method to get in-file diff information (maybe it still will for extreme differences. Annoying small things like whitespace get merged back in unless you use the -s recursive -X ignore-all-space
option)
编辑:这将合并文件,但会进行智能合并。我无法弄清楚如何使用这种方法来获取文件中的差异信息(也许它仍然会用于极端差异。除非您使用该-s recursive -X ignore-all-space
选项,否则诸如空格之类的烦人的小东西会被合并回来)
回答by Cory
1800 INFORMATION's answer is completely correct. As a git noob, though, "use git cherry-pick" wasn't enough for me to figure this out without a bit more digging on the internet so I thought I'd post a more detailed guide in case anyone else is in a similar boat.
1800 INFORMATION的回答完全正确。但是,作为一个 git noob,“使用 git cherry-pick”不足以让我在没有在互联网上进行更多挖掘的情况下弄清楚这一点,所以我想我会发布更详细的指南,以防其他人在类似的船。
My use case was wanting to selectively pull changes from someone else's github branch into my own. If you already have a local branch with the changes you only need to do steps 2 and 5-7.
我的用例想要有选择地将其他人的 github 分支中的更改拉到我自己的分支中。如果您已经有一个包含更改的本地分支,则只需执行步骤 2 和 5-7。
Create (if not created) a local branch with the changes you want to bring in.
$ git branch mybranch <base branch>
Switch into it.
$ git checkout mybranch
Pull down the changes you want from the other person's account. If you haven't already you'll want to add them as a remote.
$ git remote add repos-w-changes <git url>
Pull down everything from their branch.
$ git pull repos-w-changes branch-i-want
View the commit logs to see which changes you want:
$ git log
Switch back to the branch you want to pull the changes into.
$ git checkout originalbranch
Cherry pick your commits, one by one, with the hashes.
$ git cherry-pick -x hash-of-commit
创建(如果未创建)包含您要引入的更改的本地分支。
$ git branch mybranch <base branch>
切换进去。
$ git checkout mybranch
从其他人的帐户中拉下您想要的更改。如果您还没有,您需要将它们添加为遥控器。
$ git remote add repos-w-changes <git url>
从他们的分支上拉下所有东西。
$ git pull repos-w-changes branch-i-want
查看提交日志以查看您想要的更改:
$ git log
切换回您要将更改拉入的分支。
$ git checkout originalbranch
Cherry 用哈希值一个一个地选择你的提交。
$ git cherry-pick -x hash-of-commit
Hat tip: http://www.sourcemage.org/Git_Guide
回答by maestr0
Here is how you can replace Myclass.java
file in master
branch with Myclass.java
in feature1
branch. It will work even if Myclass.java
doesn't exist on master
.
以下是如何Myclass.java
用master
分支Myclass.java
中替换分支中的文件feature1
。即使Myclass.java
上不存在它也能工作master
。
git checkout master
git checkout feature1 Myclass.java
Note this will overwrite - not merge - and ignore local changes in the master branch rather.
请注意,这将覆盖 - 而不是合并 - 而忽略主分支中的本地更改。
回答by masukomi
The simple way, to actually mergespecific files from two branches, not just replace specific files with ones from another branch.
简单的方法,实际上合并来自两个分支的特定文件,而不仅仅是用另一个分支的文件替换特定文件。
Step one: Diff the branches
第一步:区分分支
git diff branch_b > my_patch_file.patch
git diff branch_b > my_patch_file.patch
Creates a patch file of the difference between the current branch and branch_b
创建当前分支和 branch_b 之间差异的补丁文件
Step two: Apply the patch on files matching a pattern
第二步:在匹配模式的文件上应用补丁
git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch
git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch
useful notes on the options
关于选项的有用说明
You can use *
as a wildcard in the include pattern.
您可以*
在包含模式中用作通配符。
Slashes don't need to be escaped.
斜线不需要转义。
Also, you could use --exclude instead and apply it to everything except the files matching the pattern, or reverse the patch with -R
此外,您可以改用 --exclude 并将其应用于除匹配模式的文件之外的所有内容,或使用 -R 反转补丁
The -p1 option is a holdover from the *unix patch command and the fact that the patch file's contents prepend each file name with a/
or b/
( or more depending on how the patch file was generated) which you need to strip so that it can figure out the real file to the path to the file the patch needs to be applied to.
-p1 选项是 *unix patch 命令的保留,并且补丁文件的内容在每个文件名前面加上a/
或b/
(或更多,取决于补丁文件的生成方式),您需要删除它以便它可以找出实际文件到需要应用补丁的文件的路径。
Check out the man page for git-apply for more options.
查看 git-apply 的手册页以获取更多选项。
Step three: there is no step three
第三步:没有第三步
Obviously you'd want to commit your changes, but who's to say you don't have some other related tweaks you want to do before making your commit.
显然您想提交您的更改,但谁能说您在提交之前没有其他相关的调整要做。
回答by jejese
Here's how you can get history to follow just a couple files from another branch with a minimum of fuss, even if a more "simple" merge would have brought over a lot more changes that you don't want.
以下是如何让历史记录只跟随来自另一个分支的几个文件,而不会大惊小怪,即使更“简单”的合并会带来更多您不想要的更改。
First, you'll take the unusual step of declaring in advance that what you're about to commit is a merge, without git doing anything at all to the files in your working directory:
首先,您将采取不同寻常的步骤,提前声明您将要提交的是合并,而 git 对工作目录中的文件根本不做任何事情:
git merge --no-ff --no-commit -s ours branchname1
. . . where "branchname" is whatever you claim to be merging from. If you were to commit right away, it would make no changes but it would still show ancestry from the other branch. You can add more branches/tags/etc. to the command line if you need to, as well. At this point though, there are no changes to commit, so get the files from the other revisions, next.
. . . 其中“branchname”是您声称要合并的任何内容。如果你马上提交,它不会做任何改变,但它仍然会显示来自另一个分支的血统。您可以添加更多分支/标签/等。如果需要,也可以转到命令行。不过此时,没有要提交的更改,因此接下来从其他修订版中获取文件。
git checkout branchname1 -- file1 file2 etc
If you were merging from more than one other branch, repeat as needed.
如果您从多个其他分支合并,请根据需要重复。
git checkout branchname2 -- file3 file4 etc
Now the files from the other branch are in the index, ready to be committed, with history.
现在来自另一个分支的文件在索引中,准备提交,有历史记录。
git commit
and you'll have a lot of explaining to do in that commit message.
并且您将在该提交消息中进行大量解释。
Please note though, in case it wasn't clear, that this is messed up thing to do. It is not in the spirit of what a "branch" is for, and cherry-pick is a more honest way to do what you'd be doing, here. If you wanted to do another "merge" for other files on the same branch that you didn't bring over last time, it will stop you with an "already up to date" message. It's a symptom of not branching when we should have, in the "from" branch should be more than one different branch.
但请注意,如果不清楚,这是一团糟的事情。这不符合“分支”的精神,并且在此处选择樱桃是一种更诚实的方式来做您要做的事情。如果您想对上次没有带来的同一分支上的其他文件进行另一次“合并”,它会以“已经是最新的”消息阻止您。这是我们应该有的不分支的症状,在“from”分支中应该有多个不同的分支。