我在一个新项目上使用git,该项目有两个并行 - 但目前是实验性的 - 开发分支:
master
:导入现有的代码库加上一些我一般都知道的mod
exp1
:实验分支#1
exp2
:实验分支#2
exp1
并exp2
代表两种截然不同的架构方法.直到我走得更远,我无法知道哪一个(如果有的话)会起作用.当我在一个分支中取得进展时,我有时会在另一个分支中进行编辑,并且只想合并那些.
将选择性更改从一个开发分支合并到另一个开发分支而将其他所有内容合并的最佳方法是什么?
我考虑过的方法:
git merge --no-commit
然后手动取消大量编辑,我不想在分支之间做出共同点.
手动将公共文件复制到临时目录,然后git checkout
移动到另一个分支,然后更多地手动从临时目录复制到工作树中.
以上的变化.暂时放弃exp
分支并使用另外两个本地存储库进行实验.这使得手动复制文件更加简单.
所有这三种方法都显得乏味且容易出错.我希望有更好的方法; 类似于过滤器路径参数的东西会git-merge
更具选择性.
我有与上面提到的完全相同的问题.但我在解释答案时发现这一点更清楚了.
摘要:
签出要合并的分支的路径,
$ git checkout source_branch --...
提示:它也可以--
在链接的帖子中看到.
或有选择地合并帅哥
$ git checkout -p source_branch --...
或者,使用重置然后添加选项-p
,
$ git reset... $ git add -p ...
最后提交
$ git commit -m "'Merge' these changes"
您可以使用cherry-pick命令从一个分支获取单个提交.
如果您想要的更改不在单独的提交中,那么使用此处显示的方法将提交拆分为单独的提交.粗略地说,您使用git rebase -i
原始提交进行编辑,然后git reset HEAD^
有选择地还原更改,然后git commit
将该位提交为历史记录中的新提交.
在Red Hat Magazine中还有另一个不错的方法,如果你想将不同的更改分成单个文件(在该页面中搜索"split"),他们使用git add --patch
或者可能git add --interactive
允许你只添加一部分块.
拆分更改后,您现在可以选择您想要的更改.
要有选择地将文件从一个分支合并到另一个分支,请运行
git merge --no-ff --no-commit branchX
branchX
您想要合并到当前分支的分支在哪里.
该--no-commit
选项将暂存已由Git合并而不实际提交它们的文件.这将使您有机会根据需要修改合并的文件,然后自己提交.
根据您要合并文件的方式,有四种情况:
1)你想要一个真正的合并.在这种情况下,您接受合并文件的方式是Git自动合并它们然后提交它们.
2)有些文件你不想合并.例如,您希望保留当前分支中的版本并忽略要合并的分支中的版本.
要选择当前分支中的版本,请运行:
git checkout HEAD file1
这将检索file1
当前分支的版本并覆盖file1
Git 的automerged.
跑:
git checkout branchX file1
这将检索由Git自动合并的file1
in branchX
和overrite 的版本file1
.
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
,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
.
我不喜欢上述方法.使用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 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.
虽然其中一些答案非常好,但我觉得没有人真正回答OP的原始约束:从特定分支中选择特定文件.这个解决方案可以做到这一点,但如果有很多文件可能会很乏味.
比方说你有master
,exp1
和exp2
分支机构.您希望将每个实验分支中的一个文件合并为主文件.我会做这样的事情:
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 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
以下是如何使用in branch 替换分支中的Myclass.java
文件.它即使不存在也会起作用.master
Myclass.java
feature1
Myclass.java
master
git checkout master git checkout feature1 Myclass.java
请注意,这将覆盖 - 而不是合并 - 而忽略主分支中的本地更改.
实际合并来自两个分支的特定文件的简单方法,不仅仅是将特定文件替换为另一个分支中的文件.
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的手册页以获取更多选项.
显然你想要提交你的更改,但是谁说你在提交之前没有其他相关的调整.
以下是如何让历史记录只关注来自另一个分支的几个文件,即使更简单的"合并"会带来更多您不想要的更改.
首先,您将采取不同寻常的步骤,提前声明您要提交的内容是合并,而不是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"分支中应该是不止一个不同的分支.
我知道我有点晚了,但这是我合并选择性文件的工作流程.
#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
我发现这篇文章包含了最简单的答案.仅仅做:
$ #git checkout
例:
$ #pulling .gitignore file from branchB into current branch $ git checkout branchB .gitignore
有关详细信息,请参阅帖子.
最简单的方法是将您的仓库设置为要合并的分支然后运行,
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
不适用于这种选择性合并目的.
你可以轻松地做到:
只需将此行添加到[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
这将为您提供其他分支中任何文件的真正选择性树形合并机会.
这不完全是你想要的,但它对我有用:
git checkout -p-- ...
这是一些答案的混合.
我有与上面提到的完全相同的问题.但我发现这个git博客更清楚地解释了答案.
来自以上链接的命令:
#You are in the branch you want to merge to git checkout
我会做的
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
所以情况是你想要来自'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
那git reset --soft branch
呢 我很惊讶没有人提及它。
对我来说,这是从另一分支中选择性地选择更改的最简单方法,因为此命令将所有diff更改放入我的工作树中,因此我可以轻松地选择或还原所需的内容。这样,我可以完全控制提交的文件。
我知道这个问题很老,还有很多其他答案,但是我编写了自己的脚本'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