How to selectively merge or pick changes from another branch in Git?
我在一个新项目中使用了Git,这个项目有两个并行的(但目前是实验性的)开发分支:
master :导入现有的代码库,加上一些我一般确信的模块exp1 :实验分支1exp2 :实验分支2
将选择性变更从一个开发分支合并到另一个开发分支的最佳方法是什么,而将所有其他内容都抛在脑后?
我考虑过的方法:
手动将通用文件复制到一个临时目录中,然后执行
上面的变化。暂时放弃
所有这三种方法都显得单调乏味且容易出错。我希望有一种更好的方法;类似于过滤路径参数的东西,可以使
我和你上面提到的问题完全一样。但我在解释答案时发现这一点更清楚。
总结:
从要合并的分支签出路径,
1$ git checkout source_branch -- <paths>...提示:它也可以在没有
-- 的情况下工作,就像在链接的帖子中看到的那样。或者选择性地合并大块
1$ git checkout -p source_branch -- <paths>...或者,使用重置,然后使用选项
-p 添加,1
2$ git reset <paths>...
$ git add -p <paths>...最后提交
1$ git commit -m"'Merge' these changes"
您可以使用cherry-pick命令从一个分支获取各个提交。
如果您想要的更改不在单个提交中,那么使用此处显示的方法将提交拆分为单个提交。大致来说,您使用
在Red Hat Magazine中还有另一个不错的方法,他们使用
拆分更改后,您现在可以选择所需的更改。
要有选择地将文件从一个分支合并到另一个分支,请运行
1 | git merge --no-ff --no-commit branchX |
其中,
根据合并文件的方式,有四种情况:
1)你想要一个真正的合并。在这种情况下,您接受合并文件的方式是git自动合并它们,然后提交它们。
2)有些文件您不想合并。例如,您希望在当前分支中保留版本,并忽略正在合并的分支中的版本。
要选择当前分支中的版本,请运行:
1 | git checkout HEAD file1 |
这将检索当前分支中的
运行:
1 | git checkout branchX file1 |
这将检索
在这种情况下,您可以直接编辑修改后的
如果Git不能自动合并文件,它将报告文件为"未合并",并在需要手动解决冲突的地方生成一个副本。
< BR>
为了进一步解释一个例子,假设您希望将
1 | git merge --no-ff --no-commit branchX |
然后运行
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 # |
其中
这意味着,所有这三个文件的
您可以通过运行
1 2 3 | git diff --cached file1 git diff --cached file2 git diff --cached file3 |
如果你发现一些合并不受欢迎,那么你可以
如果您不想合并
跑
1 | git checkout HEAD file1 |
如果不想合并
跑
1 | git checkout branchX file2 |
如果你想让
现在Git已经合并了它。
< BR>
上面的
< BR>
最后,别忘了给
我不喜欢上面的方法。使用cherry-pick非常适合选择单个更改,但是如果你想引入除某些坏更改之外的所有更改,这是一种痛苦。这是我的方法。
没有可以传递给git merge的
以下是备选方案:
在分支"feature"中有一些更改,您希望以一种不马虎的方式将它们中的一些(但不是全部)提交给"master"(即,您不希望对每一个进行挑选和提交)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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,就可以开始了:
1 2 3 4 5 6 7 8 9 10 11 | #!/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 |
还有另一条路要走:
1 | git checkout -p |
它是
1 2 3 4 5 6 7 8 9 | -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. |
虽然其中一些答案相当不错,但我觉得实际上没有一个回答了OP的原始约束:从特定的分支中选择特定的文件。这个解决方案可以做到这一点,但如果有许多文件,可能会很麻烦。
假设你有
1 2 3 4 5 6 7 8 | 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 |
这将为您所需的每个文件提供文件内差异。再也没有了。没什么。在不同版本之间,文件的更改是完全不同的,这很有用——在我的例子中,将应用程序从Rails2更改为Rails3。
编辑:这将合并文件,但进行智能合并。我不知道如何使用这个方法来获取文件中的差异信息(也许对于极端的差异,它仍然是如此)。如果不使用
1800信息的答案是完全正确的。不过,作为一个Git Noob,"使用Git Cherry Pick"还不足以让我在互联网上多挖一点就明白这一点,所以我想我会发布一个更详细的指南,以防其他人在类似的船上。
我的用例希望有选择地将其他人的Github分支中的更改拉到我自己的分支中。如果已经有一个本地分支进行了更改,则只需执行步骤2和5-7。
使用要引入的更改创建(如果未创建)本地分支。
切换到它。
从另一个人的帐户中下拉所需的更改。如果您还没有,您将希望将它们添加为远程。
把树枝上的东西都拉下来。
查看提交日志以查看您想要的更改:
切换回要将更改拉入的分支。
Cherry用hashes一个接一个地选择你的承诺。
帽子提示:http://www.sourcemage.org/git_指南
下面介绍如何用
1 2 | git checkout master git checkout feature1 Myclass.java |
注意,这将覆盖(而不是合并)并忽略主分支中的本地更改。
简单的方法是,实际上合并来自两个分支的特定文件,而不仅仅是用来自另一个分支的文件替换特定文件。
第一步:区分分支创建当前分支和分支之间的差异的修补文件
第二步:对匹配模式的文件应用修补程序您可以在include模式中使用
斜杠不需要逃跑。
另外,您可以使用--exclude来代替它,并将它应用于除匹配模式的文件之外的所有内容,或者使用-r反转修补程序。
-p1选项是*unix patch命令的一个保留项,事实上,补丁文件的内容会在每个文件名前面加上
查看Git Apply的手册页了解更多选项。
第三步:没有第三步显然,您希望提交您的更改,但谁会说您在提交之前没有其他相关的调整。
下面是如何让历史记录只跟踪来自另一个分支的几个文件,而不必大惊小怪,即使一个更"简单"的合并会带来很多您不想要的更改。
首先,您将采取不同寻常的步骤提前声明您将要提交的是合并,而不需要Git对工作目录中的文件做任何操作:
1 | git merge --no-ff --no-commit -s ours branchname1 |
. …其中"branchname"是您声称要合并的内容。如果你马上承诺,它不会做任何改变,但它仍然会显示出来自另一个分支的祖先。如果需要,也可以向命令行添加更多的分支/标记等。不过,此时没有要提交的更改,所以接下来从其他修订版获取文件。
1 | git checkout branchname1 -- file1 file2 etc |
如果要从多个其他分支合并,请根据需要重复。
1 | git checkout branchname2 -- file3 file4 etc |
现在,来自另一个分支的文件在索引中,准备提交,带有历史记录。
1 | git commit |
在提交消息中,您将有很多解释要做。
不过,请注意,如果不清楚,这是一个混乱的事情要做。这不是一个"分支"的目的,而樱桃采摘是一个更诚实的方式来做你将要做的,在这里。如果你想对上次没有带来的同一个分支上的其他文件执行另一个"合并",它将用一条"已经更新"消息阻止你。当我们应该有分支时,这是一个不分支的症状,在"从"分支中应该有多个不同的分支。
我发现这篇文章包含了最简单的答案。只做:
1 | $ #git checkout <branch from which you want files> <file paths> |
例子:
1 2 | $ #pulling .gitignore file from branchB into current branch $ git checkout branchB .gitignore |
更多信息请参见帖子。
我知道我有点晚了,但这是我合并选择性文件的工作流程。
1 2 3 4 5 6 7 8 9 10 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 |
最简单的方法是将repo设置为要与之合并的分支,然后运行,
1 | git checkout [branch with file] [path to file you would like to merge] |
如果你跑
1 | git status |
您将看到文件已被暂存…
然后运行
1 | git commit -m"Merge changes on '[branch]' to [file]" |
简单。
奇怪的是Git仍然没有这样一个"开箱即用"的方便工具。当更新某个旧版本分支(仍然有很多软件用户)时,我会大量使用它,只需要从当前版本分支进行一些错误修复。在这种情况下,通常需要快速地从主干中的文件中获取一些代码行,忽略许多其他更改(这些更改不应该进入旧版本)。当然,在这种情况下需要交互式的三向合并,
您可以轻松做到:
只需将这一行添加到全局
1 2 | [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;' -" |
它意味着你用得比不上。如果需要,只需更改您选择的软件。或者,如果不需要交互式选择性合并,可以将其更改为三向自动合并:
1 2 | [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;' -" |
然后这样使用:
1 | git mergetool-file <source branch> <file path> |
这将为您提供一个真正的选择性树方法,使您可以合并其他分支中的任何文件。
这不是你想要的,但对我很有用:
1 | git checkout -p <branch> -- <paths> ... |
这是一些答案的混合。
我和你上面提到的问题完全一样。但我发现这个博客在解释答案时更清晰。
来自上述链接的命令:
1 2 | #You are in the branch you want to merge to git checkout <branch_you_want_to_merge_from> <file_paths...> |
我喜欢上面的"Git Interactive Merge"答案,但有一个更简单。让Git为您使用Interactive和On的Rebase组合:
1 2 3 | A---C1---o---C2---o---o feature / ----o---o---o---o master |
因此,您需要"feature"分支(分支点"a")中的c1和c2,但目前没有其他分支。
1 2 3 | # git branch temp feature # git checkout master # git rebase -i --onto HEAD A temp |
如上所述,它将您放到交互式编辑器中,在其中为C1和C2选择"pick"行(如上所述)。保存并退出,然后它将继续执行REBASE,并为您提供分支"temp",同时指向master+c1+c2:
1 2 3 | A---C1---o---C2---o---o feature / ----o---o---o---o-master--C1---C2 [HEAD, temp] |
然后,您只需将master更新为head并删除temp分支,就可以开始了:
1 2 | # git branch -f master HEAD # git branch -d temp |
我会做一个
git diff commit1..commit2 filepattern | git-apply --index && git commit
通过这种方式,您可以限制来自分支的filepattern的提交范围。
被盗自:http://www.gelato.unsw.edu.au/archives/git/0701/37964.html
我知道这个问题很老,还有很多其他的答案,但是我编写了自己的脚本"pmerge"来部分合并目录。这是一项正在进行的工作,我仍然在学习Git和Bash脚本。
此命令使用
用法:
我没有做过广泛的测试。工作目录应该没有任何未提交的更改和未跟踪的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #!/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=$' ' # list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique for f in $(git status --porcelain -z -uno | tr '\000' ' ' | 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 |
您可以使用
1 2 3 | git remote add foo [email protected]/foo.git git fetch foo git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder |
要执行合并,请使用
另请参见:如何在Git中合并子目录?
那么
对我来说,这是从另一个分支中有选择地选择更改的最简单方法,因为这个命令将所有的diff更改放在我的工作树中,我可以轻松地选择或恢复我需要的更改。这样,我就可以完全控制提交的文件。
按文件选择合并/提交的简单方法:
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
如果您没有太多已更改的文件,这将使您没有额外的提交。
1。临时复制分支
2。重置为上次想要提交
三。从原始分支签出每个文件
现在,如果需要,您可以提交并强制push(覆盖远程)。
当两个分支的当前提交之间只有几个文件发生了更改时,我通过浏览不同的文件手动合并更改。
如果您只需要合并一个特定的目录,并保留所有其他内容,同时保留历史记录,那么您可以尝试这样做…在实验之前,从
下面的步骤假设您有两个分支
1 2 3 4 5 6 7 8 9 | 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. |