如何在git中合并子目录?

Bra*_*ler 73 git

是否可以仅将子目录的更改从本地git分支合并到远程git分支,还是"全有或全无"?

例如,我有:

branch-a
 - content-1
 - dir-1
   - content-2
Run Code Online (Sandbox Code Playgroud)

branch-b
 - content-1
 - dir-1
   - `content-2
Run Code Online (Sandbox Code Playgroud)

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

Von*_*onC 73

正如替代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 -s ours --no-commit infochimps/master
  • 仅合并infochimps/cookbooks/cassandracassandra
git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra
  • 承诺改变
 git commit -m 'merging in 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
Run Code Online (Sandbox Code Playgroud)

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

  • `git read-tree`步骤对我失败:`错误:条目'foo/bar/baz.php'与'bar/baz.php'重叠.无法绑定 (6认同)
  • 谢谢,这真棒!结肠语法,即infochimps/master:cookbooks/cassandra在某处记录了吗?我在git help read-tree中找不到它... (3认同)
  • @Martin http://git-scm.com/docs/git-rev-parse#_specifying_revisions查找`<rev>:<path>`,例如`HEAD:README`,`:README`,`master:./ README` (3认同)
  • @VonC,但不,我需要历史记录。该答案没有考虑对树中的文件进行本地修改的情况。所以需要合并。 (2认同)

Chr*_*ena 25

对于我的示例,假设您有一个分支"源"和一个分支"目标",它们都反映了它们自身的上游版本(或者不是,如果只是本地版本)并且被拉到最新的代码.假设我希望repo中的子目录名为newFeature,它只存在于'source'分支中.

git checkout destination
git checkout source newFeature/
git commit -am "Merged the new feature from source to destination branch."
git pull --rebase
git push
Run Code Online (Sandbox Code Playgroud)

比我见过的其他所有东西都要复杂得多,这对我来说很有效,在这里找到.

请注意,这不是"真正的合并",因此您不会在目标分支中拥有有关newFeature的提交信息,只会修改该子目录中的文件.但是,由于你可能会稍后将整个分支合并,或者丢弃它,这可能不是问题.

  • 它保留了历史吗? (3认同)
  • @Bibrak这种方法不会保留历史。不了解当前命令。 (2认同)

xno*_*xno 12

鉴于 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
Run Code Online (Sandbox Code Playgroud)

  • 它会在我的仓库中留下子树吗? (2认同)
  • 由于不相关的历史记录,我收到了一个错误,不幸的是 `git subtree merge` 似乎没有 `--allow-unrelated-histories` 开关,但我设法使用[此处](https://stackoverflow.txt) 中的命令。 com/a/66545626/1116364): `git merge -s subtree -Xsubtree=dir-1 temp-branch --allow-unrelated-histories` (2认同)

tob*_*yer 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
Run Code Online (Sandbox Code Playgroud)

  • 这不会合并,它会用源分支中的文件夹替换文件夹。 (2认同)

Rob*_*ert 6

用于git cherry-pick选择所需的提交并仅合并这些提交。这里的关键技巧是以简单的方式获取这些提交(这样您就不必通过手动检查 Git 日志并手动输入来找出它们)。方法如下:用于git log打印提交的SHA-1 id,如下所示:

git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>
Run Code Online (Sandbox Code Playgroud)

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

然后这样做:

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

两步,简单又稳定!

  • 这假设发现的提交仅影响相关目录。如果提交同时影响您想要合并的目录和您不希望合并的目录,那么这将导致过度挑选。 (3认同)