简体   繁体   English

如何合并 Git 中的子目录?

[英]How do I merge a sub directory in Git?

Is it possible to merge only the changes for a sub-directory from a local Git branch to a remote Git branch or is it "all or nothing"?是否可以仅将子目录的更改从本地 Git 分支合并到远程 Git 分支,还是“全有或全无”?

For example, I have:例如,我有:

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

and

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

I only want to merge the contents of branch-a dir-1 with the contents of branch-b dir-1.我只想将 branch-a dir-1 的内容与 branch-b dir-1 的内容合并。

Just as an alternative to the SO question " How do you merge selective files with git-merge? ", I just found this GitHub thread which could be more adapted for merging a whole subdirectory, based on git read-tree :作为 SO 问题“ 如何将选择性文件与 git-merge 合并? ”的替代方案,我刚刚发现了这个 GitHub 线程,它更适合基于git read-tree合并整个子目录:

  • My repository => cookbooks我的存储库 => cookbooks
    My repository target directory => cookbooks/cassandra我的存储库目标目录 => cookbooks/cassandra
  • Remote repository => infochimps远程存储库 => infochimps
    Remote repository source I want merged into cookbooks/cassandra => infochimps/cookbooks/cassandra我想合并到cookbooks/cassandra => infochimps/cookbooks/cassandra远程存储库源

Here are the commands I used to merge them这是我用来合并它们的命令

  • Add the repository and fetch it添加存储库并获取它
git remote add -f infochimps git://github.com/infochimps/cluster_chef.git
  • Perform the merge执行合并
git merge --allow-unrelated-histories -s ours --no-commit infochimps/master

(this performs a merge by using the 'ours' strategy ( -s ours ), which discards changes from the source branch. This records the fact that infochimps/master has been merged, without actually modifying any file in the target branch) (这通过使用“ours”策略( -s ours )执行合并,该策略会丢弃源分支中的更改。这记录了infochimps/master已合并的事实,而实际上并未修改目标分支中的任何文件)

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

This reads the tree for only the required source subdirectory ie cookbooks/cassandra , on the upstream branch of the source repository.这将仅读取存储库上游分支上所需的源子目录(即cookbooks/cassandra的树。

Note that the target subdirectory name should also be cookbooks/cassandra , or you would see:请注意,目标子目录名称也应该是cookbooks/cassandra ,否则您会看到:

fatal: Not a valid object name
  • Commit the change提交更改
git commit -m 'merging in infochimps cassandra'

Addendum附录

It's bizarre, [edit me] — but the read-tree step can possibly fail like this:这很奇怪, [编辑我] - 但read-tree步骤可能会像这样失败:

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

... even when both files are identical . ...即使两个文件相同 This might help:这可能有帮助:

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

But off course, verify manually that this does what you want.但是当然,请手动验证这是否符合您的要求。

For my example, assume you have a branch 'source' and a branch 'destination' which both reflect upstream versions of themselves (or not, if local only) and are pulled to the latest code.对于我的示例,假设您有一个分支 'source' 和一个分支 'destination',它们都反映了它们自己的上游版本(或者不是,如果只是本地版本)并且被拉到最新的代码。 Let's say I want the subdirectory in the repository called newFeature which only exists in the 'source' branch.假设我想要存储库中名为 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

It is significantly less convoluted than everything else I've seen and this worked perfectly for me, found here .它比我见过的其他所有东西都少得多复杂,这对我来说非常有用, 可以在这里找到

Note that this isn't a 'real merge', so you won't have the commit information about newFeature in the destination branch, just the modifications to the files in that subdirectory.请注意,这不是“真正的合并”,因此您不会在目标分支中拥有有关 newFeature 的提交信息,只有对该子目录中文件的修改。 But since you're presumably going to merge the entire branch back over later, or discard it, that might not be an issue.但是由于您可能会稍后合并整个分支,或者丢弃它,所以这可能不是问题。

Given the OP's scenario where they have two branches, but want to merge only the history of dir-1 from branch-a into branch-b :鉴于 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

I got this from a forum thread at Eclipse and it worked like a charm:我从 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

Use git cherry-pick to select the commits you want and merge only these commits.使用git cherry-pick选择您想要的提交并仅合并这些提交。 The key trick here is to get these commits in an easy way (so that you don't have to figure them out by manually checking the Git log and entering them by hand).这里的关键技巧是以简单的方式获取这些提交(这样您就不必通过手动检查 Git 日志并手动输入来弄清楚它们)。 Here's how: use git log to print the commit's SHA-1 id, like this:方法如下:使用git log打印提交的SHA-1 id,如下所示:

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

'commit-a' is the commit immediately before the start point of the branch to merge, and 'commit-b' is the last commit on the branch to merge. 'commit-a' 是要合并的分支起点之前的提交,而 'commit-b' 是要合并的分支上的最后一个提交。 '--reverse' prints these commits in reverse order for cherry-picking later. '--reverse' 以相反的顺序打印这些提交,以便稍后挑选。

Then do it like:然后这样做:

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

It is two steps, simple and stable!分两步,简单又稳定!

Create a Git repository to contain both branch-a and branch-b:创建一个包含分支 a 和分支 b 的 Git 存储库:

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

Case 0: I didn't touch the files yet案例 0:我还没有接触文件

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

This gets you the folder as it is on the other branch unstaged changes.这会为您提供其他分支上未暂存更改的文件夹。

Case 1: Team has clean commits案例 1:团队有干净的提交

If your team does clean commits, then digging around in the history for the commits could be fruitful.如果您的团队确实提交了干净的提交,那么在提交历史中挖掘可能会很有成效。 Use git cherry-pick COMMIT_HASH for those cases.在这些情况下使用git cherry-pick COMMIT_HASH You might want to git rebase -i HEAD~N where N is some number of commits to add them as pick COMMIT_HASH lines in the history if you need to.如果需要,您可能想要git rebase -i HEAD~N其中 N 是一些提交以将它们添加为历史中的pick COMMIT_HASH行。

Case 2: Clean commits are not important, but knowing the conflicts is案例 2:干净的提交并不重要,但知道冲突很重要

If you have all the commits all over the place then you likely won't need to keep the history, here's one approach that works for those cases.如果您到处都有所有提交,那么您可能不需要保留历史记录,这是一种适用于这些情况的方法。 Note that this approach will not delete files or give you the history, but it will give you the conflicts between each file.请注意,这种方法不会删除文件或为您提供历史记录,但会为您提供每个文件之间的冲突。

# 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

The easy workaround, merge and reset the undesired changes, keep what you want简单的解决方法,合并和重置不需要的更改,保留您想要的

  1. You have a repository with directory structre git/src/a , git/src/b , you only want to merge the directory b你有一个目录 structre git/src/agit/src/b的存储库,你只想合并目录b
  2. git merge origin/a-feature-branch
  3. backup the the b directory changes, for example mv b b-merged备份b目录更改,例如mv b b-merged
  4. reset the branch by command git reset HEAD --hard通过命令git reset HEAD --hard重置分支
  5. override the b directory with b-merged, mv b b-origin; mv b-merged b;用 b-merged, mv b b-origin; mv b-merged b; mv b b-origin; mv b-merged b;

This is how i would resolve it:这就是我将如何解决它:

First find the last commit that both branches have in common, this is where the two branches start to diverge:首先找到两个分支共有的最后一次提交,这是两个分支开始分歧的地方:

$ git merge-base branchA branchB
050dc022f3a65bdc78d97e2b1ac9b595a924c3f2

Then checkout a new branch from that commit:然后从该提交中签出一个新分支:

git branch branchC 050dc022f3a65bdc78d97e2b1ac9b595a924c3f2

Then checkout the folder you want to merge from source branch into the new branch:然后签出要从源分支合并到新分支的文件夹:

git checkout branchC
git checkout branchB ./path/to/the/folder/you/want/to/merge
git commit -m "update folder to merge from branchB"

Now branchC contains only changes from the folder you want to merge.现在 branchC 仅包含您要合并的文件夹中的更改。

Finally merge those changes into target branch:最后将这些更改合并到目标分支中:

git checkout branchA
git merge branchC

Now you can handle any merge conflicts as you see fit.现在您可以按您认为合适的方式处理任何合并冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM