如何在Git中合并子目录?


84

是否可以仅将子目录的更改从本地Git分支合并到远程Git分支,还是“全部或全部”?

例如,我有:

branch-a
 - content-1
 - dir-1
   - content-2

branch-b
 - content-1
 - dir-1
   - `content-2

我只想将branch-a dir-1的内容与branch-b dir-1的内容合并。


1
我认为这是一个重复: stackoverflow.com/questions/449541/...
卡尔Voigtland

Answers:


81

就像SO问题“您如何用git-merge合并选择性文件吗? ”的替代方法一样,我刚刚发现了这个GitHub线程,它可以更适合于基于git read-tree合并整个子目录:

  • 我的存储库=>cookbooks
    我的存储库目标目录=>cookbooks/cassandra
  • 远程存储库=>infochimps
    我要合并到的远程存储库源cookbooks/cassandra=>infochimps/cookbooks/cassandra

这是我用来合并它们的命令

  • 添加存储库并获取它
git remote add -f infochimps git://github.com/infochimps/cluster_chef.git
  • 执行合并
git merge --allow-unrelated-histories -s我们的--no-commit infochimps / master

(这通过使用“我们的”策略(-s ours)执行合并,该策略将丢弃源分支中的更改。这将记录infochimps/master已合并的事实,而无需实际修改目标分支中的任何文件)

  • 仅合并infochimps/cookbooks/cassandracassandra
git read-tree --prefix = cassandra / -u infochimps / master:菜谱/ cassandra

这仅读取所需的源子目录的树,即cookbooks/cassandra存储库的上游分支上。

请注意,目标子目录名称也应为cookbooks/cassandra,否则您将看到:

fatal: Not a valid object name
  • 提交更改
 git commit -m'合并到infochimps cassandra中'

附录

这很奇怪,[编辑我] -但此read-tree步骤可能会失败,如下所示:

error: Entry 'infochimps/cookbooks/cassandra/README' overlaps with 'cookbooks/cassandra/README'. Cannot bind.

...即使两个文件都相同。这可能会有所帮助:

git rm -r cassandra
git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra

但是当然,请手动验证这是否符合您的要求。


3
@马丁git-scm.com/docs/git-rev-parse#_specifying_revisions寻找<rev>:<path>,比如HEAD:README:READMEmaster:./README
VonC

6
git read-tree步骤对我而言失败:error: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
Weston Ruter 2013年

1
@VonC但没有,我需要历史记录。该答案不考虑在树中对文件进行本地修改的情况。因此,它需要合并。
韦斯顿·鲁特

对我来说,上述overlaps with错误也会在相同文件上弹出。那有多奇怪?
ulidtko

1
@ChrisHalcrow记录infochimps/master已合并的事实,但没有实际修改目标分支中的任何文件。因为下一步git read-tree --prefix=cassandra将做修改。最后的提交将记录实际的“合并”内容。
VonC

29

在我的示例中,假设您有一个分支“源”和一个分支“目的地”,它们既反映了自己的上游版本(或者不反映,如果仅是本地的),并被拉到最新的代码。假设我想要存储库中名为newFeature的子目录,该子目录仅存在于“源”分支中。

git checkout destination
git checkout source newFeature/
git commit -am "Merged the new feature from source to destination branch."
git pull --rebase
git push

它比我所见过的其他任何东西都要复杂得多,这对我来说很完美,在这里可以找到

请注意,这不是“真正的合并”,因此,目标分支中不会包含有关newFeature的提交信息,而仅是对该子目录中文件的修改。但是,由于您可能稍后将合并整个分支,或者将其丢弃,因此这可能不是问题。


2
它保存历史吗?
Bibrak

2
@Bibrak这种方法不会保留历史。不了解当前命令。
拉维·吉德瓦尼

6

我是从Eclipse的一个论坛线程获得的,它的工作原理很吸引人:

git checkout source-branch
git checkout target-branch <directories-or-files-you-do-**NOT**-want> 
git commit
git checkout target-branch
git merge source-branch

1
我尝试了此操作,最后在目标分支的源分支中找到了一个我不想要的目录。尽管已指定了一组Dirs,但我在第二个命令中并不想。
马拉松赛

2
这不会合并,而是用源分支中的文件夹替换该文件夹。
Gp2mv3

@ Gp2mv3我认为它看起来很牢固。checkout使文件夹相同=>区别只是取消checkout文件夹=>merge区别。这是一个有效的策略。
穴居人

5

给定OP的场景,他们有两个分支,但只想将dir-1的历史记录从branch-a合并到branch-b中

# Make sure you are in the branch with the changes you want
git checkout branch-a

# Split the desired folder into its own temporary branch
# This replays all commits, so it could take a while
git subtree split -P dir-1 -b temp-branch

# Enter the branch where you want to merge the desired changes into
git checkout branch-b

# Merge the changes from the temporary branch
git subtree merge -P dir-1 temp-branch

# Handle any conflicts
git mergetool

# Commit
git commit -am "Merged dir-1 changes from branch-a"

# Delete temp-branch
git branch -d temp-branch

0

创建一个Git存储库以同时包含branch-a和branch-b:

git checkout branch-a
git diff branch-b dir-1 > a.diff
patch -R -p1 < a.diff

14
该答案需要更多信息。这是什么实际代码与注释?
qodeninja

2
请求者想要合并。使用补丁进行更改会自动将所有提交压缩为一个补丁,并且历史记录将丢失。
埃里克(Eric)

0

使用git cherry-pick来选择您想要的提交和合并只有这些提交。这里的关键技巧是以一种简单的方式获得这些提交(这样您就不必通过手动检查Git日志并手动输入来弄清楚它们)。方法如下:用于git log打印提交的SHA-1 ID,如下所示:

git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>

“ commit-a”是要合并的分支的起点之前的提交,“ commit-b”是要合并的分支的最后一个提交。'--reverse'以相反的顺序打印这些提交,以供日后挑选。

然后像这样:

git cherry-pick $(git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>)

这是两个步骤,简单而稳定!


合并来自另一个分支的单个目录并保留历史记录的极好且唯一的方法。但是......如果你只想要一个承诺,从第一个命令所有提交创建一个分支,然后合并新分支在一个壁球合并....

1
假设找到的提交仅影响相关目录。如果一个提交同时影响到您要合并的目录和您不想合并的目录,这将过多地挑剔。
斯科特

Two steps, simple and stable!一点都不简单:)
拉法

那么您认为哪种方法很简单?@Rafa
罗伯特

@Robert我不是要暗示您的回答并不简单;故障全在上面git,它具有可怕的界面和可怕的心理模型,里面充斥着各种谜团,随意的名称和含义。
拉法
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.