当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在Git中有选择地合并或选择来自另一个分支的更改?》经验,为你挑选了20个好方法。

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

master:导入现有的代码库加上一些我一般都知道的mod

exp1:实验分支#1

exp2:实验分支#2

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

将选择性更改从一个开发分支合并到另一个开发分支而将其他所有内容合并的最佳方法是什么?

我考虑过的方法:

    git merge --no-commit 然后手动取消大量编辑,我不想在分支之间做出共同点.

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

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

所有这三种方法都显得乏味且容易出错.我希望有更好的方法; 类似于过滤器路径参数的东西会git-merge更具选择性.



1> Susheel Java..:

我有与上面提到的完全相同的问题.但我在解释答案时发现这一点更清楚了.

摘要:

签出要合并的分支的路径,

$ git checkout source_branch -- ...

提示:它也可以--在链接的帖子中看到.

或有选择地合并帅哥

$ git checkout -p source_branch -- ...

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

$ git reset ...
$ git add -p ...

最后提交

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


这不是真正的合并.您按文件而不是按提交选择更改,您将丢失任何现有的提交信息(作者,消息).当然,如果你想合并一些文件中的所有更改,这很好,你必须重新做所有提交.但是,如果文件包含要合并的更改和其他要丢弃的更改,则其他答案中提供的方法之一将更好地为您服务.
看看你也可以使用的变化:`git diff --cached`
@mykhal和其他人:这会自动对索引中的文件进行分阶段,所以如果你检查了`foo.c`,请执行`git reset HEAD foo.c`来取消暂存该文件,然后你可以对其进行区分.我在尝试之后发现了这一点并回到这里寻找答案
Bart J的链接文章是最好的方法.清晰,简单,一个命令.这是我即将使用的那个.:)
根据这个[回答](http://stackoverflow.com/a/13712429/2458234)`git checkout -p - `将与你描述的前三个命令相同:)
@spacemanaki有我认为最好的答案.`git checkout feature-branch foo.c`后跟`git reset HEAD foo.c`然后是`git add -p`来引入你想要的差异或排除坏的,然后`git commit foo.c`来保存您的更改,并使用`git checkout foo.c`重置索引.工作就像一个魅力.
@Pistos,我在这个页面上找不到'Bart J'.也许自2009年10月以来用户名/等已经发生了变化?
这不是真正的合并,但它正是我想要的!

2> 1800 INFORMA..:

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

如果您想要的更改不在单独的提交中,那么使用此处显示的方法将提交拆分为单独的提交.粗略地说,您使用git rebase -i原始提交进行编辑,然后git reset HEAD^有选择地还原更改,然后git commit将该位提交为历史记录中的新提交.

在Red Hat Magazine中还有另一个不错的方法,如果你想将不同的更改分成单个文件(在该页面中搜索"split"),他们使用git add --patch或者可能git add --interactive允许你只添加一部分块.

拆分更改后,您现在可以选择您想要的更改.


从技术上讲,这是正确的答案,正确的答案确实显得"错综复杂".---更高的投票答案只是一个快速而肮脏的"诀窍"答案,对大多数人来说就是他们所关注的(:
根据我的理解,这比投票得多的答案更令人费解.
我只是想分享另一种方法,这种方法看起来最干净,最不那么令人费解:http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-另一个分支/完全简单和令人敬畏
关于哪种方法"正确"的辩论感到困惑?考虑[文件和提交之间的差异(参见底部的备注)](http://stackoverflow.com/documentation/git/244/staging#t=20160826045846751695).OP希望合并FILES并且不提及COMMITS.投票率较高的答案特定于文件; 接受的答案使用cherry-pick,这是特定于提交.Cherry-pick可能是有选择地合并提交的关键,但将文件从一个分支移动到另一个分支可能会非常痛苦.虽然提交是git强项的核心,但不要忘记文件仍有作用!
@akaihola:HEAD ^是正确的.请参阅man git-rev-parse:修订参数的后缀^表示该提交对象的第一个父级.前缀^表示法用于排除从提交可到达的提交.

3> 小智..:

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

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

branchX您想要合并到当前分支的分支在哪里.

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

根据您要合并文件的方式,有四种情况:

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

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

2)有些文件你不想合并.

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

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

git checkout HEAD file1

这将检索file1当前分支的版本并覆盖file1Git 的automerged.

3)如果你想在branchX中使用该版本(而不是真正的合并).

跑:

git checkout branchX file1

这将检索由Git自动合并的file1in branchX和overrite 的版本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 ..." as appropriate to mark resolution)
#
#       both modified:      file4
#

哪里file1,file2file3,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.


与得票最多的答案不同,这个解决方案保留了我的合并历史记录,这对我很重要,因为我在分支机构之间来回编织部分提交.我没有尝试所有其他建议的解决方案,所以也许他们中的一些也这样做.
@cfi如何添加`--no-ff`以防止这种行为?
但要小心:如果`git merge --no-commit branchX`只是一个快进,指针将被更新,因此--no-commit被忽略
该解决方案可提供最佳结果和灵活性.
我绝对建议用Eduardo的"--no-ff"选项更新这个答案.我仔细阅读了整个事情(其他方面很棒)只是为了让我的合并得到快速转发.
小心.此合并在一个方向上工作,但如果您决定将上游主服务器合并回源分支,则合并中未包含的文件将被视为DELETED.我使用git-flow类进程使用主分支(生产主线),登台分支(登台服务器主线)和基于登台分支的主题分支.使用这种策略导致我从主服务器"反向合并"回到暂存状态,从而完全失败,认为我没有从阶段到主服务器合并的所有内容都被删除.这包括整个文件和帅哥.你被警告了

4> nosatalian..:

我不喜欢上述方法.使用cherry-pick非常适合选择单个更改,但如果您想引入除了一些不良更改之外的所有更改,那将是一种痛苦.这是我的方法.

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

这是替代方案:

你在分支'特征'中有一些变化,你想要以一种不邋way的方式将一些但不是全部的变为'master'(即你不想挑选并提交每一个)

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并将功能更改为$ 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`等.还值得用"&&"而不是换行链接这些行.
不幸的是,似乎答案中的链接已经死了.

5> Chronial..:

还有另外一种方法:

git checkout -p

这是之间的混合git checkout,并git add -p与可能相当地正是你正在寻找:

   -p, --patch
       Interactively select hunks in the difference between the 
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
        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
真正最简单的方法。git checkout -p featurebranch文件名。最好的是,当命令运行时,它会给您ay / n / e /?/ ...等。决定如何合并文件的选项。我尝试使用e,甚至可以在应用之前编辑补丁。多么酷。真正的一个班轮,用于合并其他分支机构的选择性文件。

6> Eric Hu..:

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

比方说你有master,exp1exp2分支机构.您希望将每个实验分支中的一个文件合并为主文件.我会做这样的事情:

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

这将为您提供所需的每个文件的文件内差异.而已.没什么.在版本之间进行完全不同的文件更改很有用 - 在我的例子中,将应用程序从Rails 2更改为Rails 3.

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


另请注意:您可以从所有内联的分支执行多个文件,例如`git checkout exp1 path/to/file_a path/to/file_x`
这很漂亮.我做了`git checkout feature /*`来获取文件组.
哦,我觉得这很清楚.这样它就可以合并一个文件,而不必覆盖目标分支上的先前更改.

7> Cory..:

1800 INFORMATION的答案完全正确.然而,作为一个git noob,"使用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

    樱桃用哈希一个接一个地挑选你的提交.

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

帽子提示:http://www.sourcemage.org/Git_Guide


提示:首先使用`git cherry`命令(请参见手册)来识别尚未合并的提交.

8> maestr0..:

以下是如何使用in branch 替换分支中的Myclass.java文件.它即使不存在也会起作用.masterMyclass.javafeature1Myclass.javamaster

git checkout master
git checkout feature1 Myclass.java

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


这不会合并.它只会使用feature1分支的更改覆盖master上的更改.
完美的是,我正在寻找这种合并,其中`theirs`覆盖`ours` => +1干杯;)

9> masukomi..:

实际合并来自两个分支的特定文件的简单方法,不仅仅是将特定文件替换为另一个分支中的文件.

第一步:扩散分支

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 patch命令的保留,以及补丁文件的内容在每个文件名前面加上a/或者b/(或者更多取决于补丁文件的生成方式)你需要删除它以便它可以弄清楚这一事实真实文件到补丁需要应用的文件路径.

查看git-apply的手册页以获取更多选项.

第三步:没有第三步

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



10> jejese..:

以下是如何让历史记录只关注来自另一个分支的几个文件,即使更简单的"合并"会带来更多您不想要的更改.

首先,您将采取不同寻常的步骤,提前声明您要提交的内容是合并,而不是git对工作目录中的文件执行任何操作:

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

...其中"branchname"是你声称要合并的东西.如果你马上提交,它将不做任何改变,但它仍会显示来自另一个分支的祖先.您可以添加更多分支/标签/等.如果需要,也可以到命令行.但是,此时提交没有更改,因此请从其他修订中获取文件.

git checkout branchname1 -- file1 file2 etc

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

git checkout branchname2 -- file3 file4 etc

现在,来自其他分支的文件位于索引中,准备提交,具有历史记录.

git commit

你会在提交消息中做很多解释.

但请注意,如果不清楚,这是搞乱的事情要做.它不符合"分支"的精神,而樱桃选择是一种更诚实的方式来做你在做的事情.如果你想为你上次没有带来的同一个分支上的其他文件做另一个"合并",那么它将以"已经是最新的"消息阻止你.这是我们应该拥有的不分支的症状,在"from"分支中应该是不止一个不同的分支.


你的第一个命令(`git merge --no-ff --no-commit -s outs branchname1`)正是我想要的!谢谢!

11> Felix..:

我知道我有点晚了,但这是我合并选择性文件的工作流程.

#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> Stunner..:

我发现这篇文章包含了最简单的答案.仅仅做:

$ #git checkout  

例:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

有关详细信息,请参阅帖子.


这并没有真正合并,它会覆盖当前分支上的文件.

13> dsieczko..:

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

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

如果你跑

git status

你会看到文件已经上演了......

然后跑

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

简单.


这是我发现的几乎最好的答案.请参阅http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/如此清晰,简洁,它只是有效!

14> 小智..:

奇怪的是,git仍然没有"开箱即用"的便利工具.我只是通过当前版本分支的一些错误修正来更新一些旧版本分支(它仍然有很多软件用户)时大量使用它.在这种情况下,经常需要从trunk中的文件中快速获取一些代码行,忽略了许多其他更改(不应该进入旧版本)...当然还有交互式三向合并在这种情况下需要,git checkout --patch 不适用于这种选择性合并目的.

你可以轻松地做到:

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

[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  

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



15> Felipe..:

这不完全是你想要的,但它对我有用:

git checkout -p  --  ...

这是一些答案的混合.



16> Susheel Java..:

我有与上面提到的完全相同的问题.但我发现这个git博客更清楚地解释了答案.

来自以上链接的命令:

#You are in the branch you want to merge to
git checkout  



17> lumpidu..:

我会做的

git diff commit1..commit2 filepattern | git-apply --index && git commit

这样,您可以限制来自分支的文件模式的提交范围.

被盗:http://www.gelato.unsw.edu.au/archives/git/0701/37964.html



18> Wade..:

我喜欢上面的'git-interactive-merge'答案,但有一个更简单.让git使用交互式的rebase组合为你做这个:

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

所以情况是你想要来自'feature'分支的C1和C2(分支点'A'),但现在不需要其余的.

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

如上所述,您将进入交互式编辑器,在该编辑器中为C1和C2选择"拾取"行(如上所述).保存并退出,然后它将继续使用rebase并给你分支'temp'和HEAD在master + C1 + C2:

      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



19> GarryOne..:

git reset --soft branch呢 我很惊讶没有人提及它。

对我来说,这是从另一分支中选择性地选择更改的最简单方法,因为此命令将所有diff更改放入我的工作树中,因此我可以轻松地选择或还原所需的内容。这样,我可以完全控制提交的文件。



20> Andy..:

我知道这个问题很老,还有很多其他答案,但是我编写了自己的脚本'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 w 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

推荐阅读
N个小灰流_701
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有