【问题标题】:How do I merge a sub directory in Git?如何在 Git 中合并子目录?
【发布时间】:2010-11-15 22:33:48
【问题描述】:

是否可以仅将子目录的更改从本地 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 的内容。

【问题讨论】:

标签: git


【解决方案1】:

案例0:我还没有碰过文件

git checkout origin/branch-with-the-code-you-want ./path/to/dir1

这会为您获取另一个分支上的文件夹,因为它是未暂存的更改。

案例 1:团队有干净的提交

如果您的团队确实进行了干净的提交,那么在历史中挖掘提交可能会很有成效。对于这些情况,请使用 git cherry-pick COMMIT_HASH。您可能想要git rebase -i HEAD~N,其中 N 是一些提交,如果需要,可以将它们添加为历史记录中的 pick COMMIT_HASH 行。

案例 2:干净的提交并不重要,但知道冲突是

如果您在所有地方都有所有提交,那么您可能不需要保留历史记录,这是一种适用于这些情况的方法。请注意,这种方法不会删除文件或为您提供历史记录,但会为您提供每个文件之间的冲突。

# Create an orphan branch with no history but your files
git checkout --orphan temp-branch

# Get the version of the files from the other branch
git checkout origin/branch-with-the-changes ./path/to/the/folder/you/want/to/merge
git commit -m "Commit that has all files like they are on your branch, except that one folder you want to merge"

# Merge the other file tree
git checkout your-branch
git merge temp-branch --allow-unrelated

# Clean up
git branch -D temp-branch

【讨论】:

    【解决方案2】:

    考虑到 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
    

    【讨论】:

    • 我可以使用多个目录吗?
    • 它会在我的仓库中留下子树吗?
    • @nr5 - 您可能只需对要合并其历史记录的每个目录重复此过程
    【解决方案3】:

    使用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>)
    

    就是两步,简单又稳定!

    【讨论】:

    • 从另一个分支合并单个目录并保留历史记录的绝佳且唯一的方法。但是... ...如果您只想要一个提交,请从第一个命令的所有提交中创建一个分支,然后将该新分支合并到一个壁球合并中......
    • 这假设找到的提交只影响有问题的目录。如果一个提交同时影响了你想要合并的目录和你不想合并的目录,那么这会选择太多。
    • Two steps, simple and stable! 一点也不简单 :)
    • 那么你认为哪种方式简单? @拉法
    • @Robert 我并不是要暗示你的答案并不简单;错误都在git,它有一个糟糕的界面和糟糕的心理模型,充满了谜团和随机的名称和含义。
    【解决方案4】:

    在我的示例中,假设您有一个分支“源”和一个分支“目标”,它们都反映了它们自身的上游版本(或不反映它们自身的上游版本,如果仅是本地版本)并且被拉到最新的代码。假设我想要存储库中名为 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
    

    它比我见过的其他所有东西都少得多,这对我来说非常有效,found here

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

    【讨论】:

    • 是否保留历史记录?
    • @Bibrak 这种方法不会保留历史记录。至少不是当前的命令。
    【解决方案5】:

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

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

    【讨论】:

    • 这个答案需要更多信息。与 cmets 相比,这是什么实际代码?
    • 请求者想要合并。使用补丁来继承更改会自动将所有提交压缩到一个补丁中,并且历史记录会丢失。
    • 答案简短明了。它清楚地显示了如何合并单个目录,这对我很有用。请求者没有说历史很重要。此外,如果从所有提交中提取单个目录,我会说这意味着新的历史是从头开始的。
    【解决方案6】:

    作为 SO 问题“How do you merge selective files with git-merge?”的替代方案,我刚刚发现 this GitHub thread 更适合合并整个子目录,基于 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 ours --no-commit infochimps/master

    (这通过使用'ours'策略(-s ours)执行合并,丢弃来自源分支的更改。这记录了infochimps/master已被合并的事实,而没有实际修改目标分支中的任何文件)

    • 仅将infochimps/cookbooks/cassandra 合并到cassandra
    git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra

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

    请注意,target 子目录名称也应该是 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
    

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

    【讨论】:

    • @Martin git-scm.com/docs/git-rev-parse#_specifying_revisions 寻找 &lt;rev&gt;:&lt;path&gt;,例如HEAD:README, :README, master:./README
    • git read-tree 步骤对我来说失败了:error: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
    • @VonC 但不,我需要历史记录。该答案不考虑对树中的文件进行本地修改的情况。所以需要合并。
    • 对我来说,上面提到的overlaps with 错误也会在相同的文件上弹出。这有多奇怪?
    • @ChrisHalcrow 记录infochimps/master已经被合并,但没有实际修改目标分支中的任何文件。因为下一步git read-tree --prefix=cassandra会做修改。最终的提交将记录实际的“合并”内容。
    【解决方案7】:

    我从 Eclipse 的 forum thread 那里得到了这个,它就像一个魅力:

    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
    

    【讨论】:

    • 我尝试了这个并最终得到了我不想要的目标分支中源分支的目录。尽管在第二个命令中指定了一组我不想要的目录。
    • 这不会合并,它将文件夹替换为源分支中的文件夹。
    • @Gp2mv3 我认为它看起来很可靠。 checkout 使文件夹相同 => 区别只是 un-checkout 文件夹 => merge 不同。这是一个有效的策略。
    猜你喜欢
    • 2011-09-19
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    相关资源
    最近更新 更多