你如何合并select性文件与Git合并?

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

  • master :导入现有的代码库,加上我通常确定的几个mods
  • exp1 :实验分支#1
  • exp2 :实验分支#2

exp1exp2代表了两种非常不同的架构方法。 直到我走得更远,我无法知道哪一个(如果有)将工作。 当我在一个分支中取得进展时,我有时会编辑一些对其他分支有用的编辑,并希望仅合并这些编辑。

将select性文件从一个开发分支合并到另一个开发分支的最好方法是什么?

我已经考虑的方法:

  1. git merge --no-commit接下来是大量的编辑操作,我不想在分支之间进行共同操作。

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

  3. 以上的变化。 现在放弃exp分支并使用另外两个本地存储库进行实验。 这使得手动复制文件变得更直接。

所有这三种方法都显得乏味且容易出错。 我希望有一个更好的方法; 类似于使git-merge更具select性的filterpath参数。

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

如果所需的更改不在单个提交中,则使用此处显示的方法将提交拆分为单独的提交 。 粗略地说,你使用git rebase -i来获得原始的提交来编辑,然后git reset HEAD^select性地还原更改,然后git commit提交该位作为历史上的新提交。

在Red Hat杂志中还有一个很好的方法 ,那就是使用git add --patch或者git add --interactive ,如果你想把不同的改变分割成单独的文件(search在该页面的“拆分”)。

把这些变化分开后,你现在可以挑选你想要的。

我有上面提到的完全相同的问题。 但是我更清楚地解释了答案。

概要:

  • 检出您要合并的分支的path,

     $ git checkout source_branch -- <paths>... 
  • 或有select性地合并暴利

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

    另外,使用重置,然后添加选项-p

     $ git reset <paths>... $ git add -p <paths>... 
  • 最后提交

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

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

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

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

--no-commit选项将--no-commit由Git合并的文件,而不实际提交它们。 这将使您有机会修改合并文件,然后自行提交。

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

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

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

2)有一些你不想合并的文件。

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

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

 git checkout HEAD file1 

这将检索当前分支中的file1版本,并通过Git覆盖file1 automerged。

3)如果你想在branchX版本(而不是真正的合并)。

跑:

 git checkout branchX file1 

这将检索branchXfile1版本,并覆盖Git自动合并的file1

4)最后一种情况是,如果只想在file1select特定的合并。

在这种情况下,您可以直接编辑修改后的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 

如果你发现一些合并不可取,那么你可以

  1. 直接编辑文件
  2. 保存
  3. git commit

如果您不想合并file1并想要保留当前分支中的版本

 git checkout HEAD file1 

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

 git checkout branchX file2 

如果你想file3自动合并,不要做任何事情。

Git已经合并在这一点上。

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

最后,不要忘记git commit

我不喜欢上述方法。 使用樱桃挑选是一个很好的select一个单一的变化,但它是一个痛苦,如果你想引入所有的变化,除了一些不好的。 这是我的方法。

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

这是另一种方法:

你在分支的“function”上有一些变化,你想把一些但不是全部的东西都带到“主”中去(比如你不想挑选和提交每一个)

 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,然后将function更改为$ 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 checkout -p 

这是一个git checkout之间的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. 

虽然其中一些答案相当不错,但我觉得没有人真正回答OP的原始约束:从特定的分支中select特定的文件。 这个解决scheme是这样做的,但是如果有很多文件,可能会很乏味。

假设你有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 

这将为您提供所需的每个文件的文件差异。 而已。 没有什么。 这是有用的,你有根本不同的文件之间的变化版本 – 在我的情况下,从Rails 2更改为Rails 3的应用程序。

编辑 :这将合并文件,但做了一个聪明的合并。 我无法弄清楚如何使用这种方法来获取文件中的差异信息(也许它仍然会出现极大的差异,像空白这样的小事情会被重新合并回来,除非你使用-s recursive -X ignore-all-space选项)

1800信息的答案是完全正确的。 作为一个git noob,虽然“使用git cherry-pick”对于我来说是不够的,没有多less在互联网上的挖掘,所以我想我会发布一个更详细的指导,以防其他人在一个类似的船。

我的用例是想要有select地从其他人的github分支变更到我自己的。 如果您已经有一个本地分支进行更改,则只需执行步骤2和步骤5-7。

  1. 创build(如果没有创build)本地分支与你想要带来的变化。

    $ git branch mybranch <base branch>

  2. 切换到它。

    $ git checkout mybranch

  3. 从其他人的帐户中取消所需的更改。 如果你还没有,你会想要添加他们作为一个远程。

    $ git remote add repos-w-changes <git url>

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

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

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

    $ git log

  6. 切换回您想要将更改引入的分支。

    $ git checkout originalbranch

  7. 樱桃挑选你的提交,一个接一个,用哈希。

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

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

以下是如何在feature1分支中用Myclass.javareplacemaster分支中的Myclass.java文件。 即使Myclass.javamaster上不存在,它也可以工作。

 git checkout master git checkout feature1 Myclass.java 

注意这将覆盖 – 不合并 – 而忽略主分支中的本地更改。

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

第一步:比较分支

git diff branch_b > my_patch_file.patch

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

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

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

有用的备注关于选项

你可以在include模式中使用*作为通配符。

斜线不需要被转义。

另外,你也可以使用–exclude,并将其应用于除了匹配模式的文件之外的所有文件,或者使用-R反转该补丁

-p1选项是* unix patch命令的保留,并且补丁文件的内容在每个文件名前加上a/b/ (或更多,取决于补丁文件的生成方式),因此需要删除它可以找出真正的文件到补丁需要应用到的文件的path。

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

第三步:没有第三步

很明显,你会想要提交你的修改,但是在提交之前,你还没有做一些其他相关的调整。

以下是如何让历史logging只需从另一个分支中按几个文件进行操作,即使更简单的合并也会带来更多不必要的更改。

首先,你将采取不寻常的步骤,事先声明你将要提交的东西是一个合并,没有git对你的工作目录中的文件做任何事情:

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

。 。 。 “branchname”是你声称要合并的任何东西。 如果你立即承诺,它将不会有任何改变,但仍然会显示另一个分支的祖先。 你可以添加更多的分支/标签/等。 如果你需要,也可以到命令行。 在这一点上,没有改变提交,所以从其他修订,接下来的文件。

 git checkout branchname1 -- file1 file2 etc 

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

 git checkout branchname2 -- file3 file4 etc 

现在来自其他分支的文件在索引中,随时可以提交,有历史logging。

 git commit 

在这个提交信息中你会做很多解释。

但是请注意,如果不清楚的话,这是搞砸的事情。 这不符合什么“分支”的精神,select樱桃是一个更诚实的方式来做你在做什么,在这里。 如果您想要对上一次没有拿过的同一分支上的其他文件进行另一个“合并”,它将会以“已经更新”的消息来阻止您。 我们应该有一个不分枝的症状,在“从”分支应该是不止一个分支。

我知道我有点晚了,但这是我合并select性文件的工作stream程。

 #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 

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

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

例:

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

查看post了解更多信息。

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

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

如果你跑步

 git status 

你会看到文件已经上演…

然后运行

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

简单。

奇怪的是,git仍然没有“开箱即用”的方便工具。 我更新一些旧版本分支(它仍然有很多软件用户), 只是通过从当前版本分支的一些错误修正而使用它。 在这种情况下,通常需要从trunk中的文件中快速获取一些代码行,忽略其他更改(不应该进入旧版本)…当然, 交互式三路合并在这种情况下是需要的, git checkout --patch <branch> <file path>不可用于此select性合并目的。

你可以轻松做到这一点:

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

 [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。 如果需要,只需转换到您select的软件。 或者,如果您不需要交互式select性合并,则可以将其更改为三向自动合并:

 [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> 

这将给你在其他分支的任何文件的真正的select性树状合并机会。

我会做一个

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

这样你可以限制一个分支文件模式的提交范围。

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

我喜欢上面的'git-interactive-merge'的答案,但是有一个更容易。 让git为您使用交互式的rebase组合,并转到:

  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 

如上所述,您将进入交互式编辑器,您可以在其中selectC1和C2的“拾取”行(如上所述)。 保存并退出,然后它将进行rebase并给你分支'临时',也在主+ C1 + C2头:

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

然后,您可以将主人更新到HEAD,并删除临时分支,你很好去:

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

我知道这个问题很老,还有很多其他的答案,但我写了一个名为“pmerge”的脚本来部分合并目录。 这是一个正在进行的工作,我仍然在学习git和bash脚本。

这个命令使用git merge --no-commit ,然后取消不符合提供的path的更改。

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

我没有广泛的testing。 工作目录应该没有任何未提交的更改和未跟踪的文件。

 #!/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 

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

从上面的链接命令:

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

这不是你正在寻找的东西,但它对我有用:

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

这是一些答案的组合。

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

另请参阅: 如何在git中合并子目录?

当两个分支的当前提交之间只有less量文件发生变化时,我通过不同的文件手动合并变更。

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