你如何合并select性文件与Git合并?
我在一个新的项目上使用git,这个项目有两个平行但是目前是实验性的开发分支:
-
master
:导入现有的代码库,加上我通常确定的几个mods -
exp1
:实验分支#1 -
exp2
:实验分支#2
exp1
和exp2
代表了两种非常不同的架构方法。 直到我走得更远,我无法知道哪一个(如果有)将工作。 当我在一个分支中取得进展时,我有时会编辑一些对其他分支有用的编辑,并希望仅合并这些编辑。
将select性文件从一个开发分支合并到另一个开发分支的最好方法是什么?
我已经考虑的方法:
-
git merge --no-commit
接下来是大量的编辑操作,我不想在分支之间进行共同操作。 -
手动将常用文件复制到临时目录中,然后将
git checkout
移动到另一个分支,然后将更多的手动拷贝出temp目录到工作树中。 -
以上的变化。 现在放弃
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
这将检索branchX
的file1
版本,并覆盖Git自动合并的file1
。
4)最后一种情况是,如果只想在file1
select特定的合并。
在这种情况下,您可以直接编辑修改后的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 #
其中file1
, file2
和file3
是git已成功自动合并的文件。
这意味着所有这三个文件的master
和branchX
中的更改已经组合在一起,没有任何冲突。
您可以通过运行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
。
我不喜欢上述方法。 使用樱桃挑选是一个很好的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 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.
虽然其中一些答案相当不错,但我觉得没有人真正回答OP的原始约束:从特定的分支中select特定的文件。 这个解决scheme是这样做的,但是如果有很多文件,可能会很乏味。
假设你有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
这将为您提供所需的每个文件的文件差异。 而已。 没有什么。 这是有用的,你有根本不同的文件之间的变化版本 – 在我的情况下,从Rails 2更改为Rails 3的应用程序。
编辑 :这将合并文件,但做了一个聪明的合并。 我无法弄清楚如何使用这种方法来获取文件中的差异信息(也许它仍然会出现极大的差异,像空白这样的小事情会被重新合并回来,除非你使用-s recursive -X ignore-all-space
选项)
1800信息的答案是完全正确的。 作为一个git noob,虽然“使用git cherry-pick”对于我来说是不够的,没有多less在互联网上的挖掘,所以我想我会发布一个更详细的指导,以防其他人在一个类似的船。
我的用例是想要有select地从其他人的github分支变更到我自己的。 如果您已经有一个本地分支进行更改,则只需执行步骤2和步骤5-7。
-
创build(如果没有创build)本地分支与你想要带来的变化。
$ 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
-
樱桃挑选你的提交,一个接一个,用哈希。
$ git cherry-pick -x hash-of-commit
帽子提示: http : //www.sourcemage.org/Git_Guide
以下是如何在feature1
分支中用Myclass.java
replacemaster
分支中的Myclass.java
文件。 即使Myclass.java
在master
上不存在,它也可以工作。
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>