在子目录中合并 git 存储库
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6426247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Merge git repository in subdirectory
提问by christosc
I'd like to merge a remote git repository in my working git repository as a subdirectory of it. I'd like the resulting repository to contain the merged history of the two repositories and also that each file of the merged-in repository retain its history as it was in the remote repository. I tried using the subtree strategy as mentioned in How to use the subtree merge strategy, but after following that procedure, although the resulting repository contains indeed the merged history of the two repositories, individual files coming from the remote one haven't retained their history (`git log' on any of them just shows a message "Merged branch...").
我想在我的工作 git 存储库中合并一个远程 git 存储库作为它的子目录。我希望生成的存储库包含两个存储库的合并历史记录,并且合并存储库的每个文件都保留其在远程存储库中的历史记录。我尝试使用如何使用子树合并策略中提到的子树策略,但是在执行该过程之后,尽管生成的存储库确实包含两个存储库的合并历史记录,但来自远程存储库的单个文件并未保留其历史记录(其中任何一个的`git log'只显示一条消息“合并分支......”)。
Also I don't want to use submodules because I do not want the two combined git repositories to be separate anymore.
此外,我不想使用子模块,因为我不想再将两个合并的 git 存储库分开。
Is it possible to merge a remote git repository in another one as a subdirectory with individual files coming from the remote repository retaining their history?
是否可以将远程 git 存储库中的远程 git 存储库合并为一个子目录,其中来自远程存储库的单个文件保留其历史记录?
Thanks very much for any help.
非常感谢您的帮助。
EDIT: I'm currently trying out a solution that uses git filter-branch to rewrite the merged-in repository history. It does seem to work, but I need to test it some more. I'll return to report on my findings.
编辑:我目前正在尝试使用 git filter-branch 重写合并存储库历史的解决方案。它似乎确实有效,但我需要再测试一下。我会回来报告我的发现。
EDIT 2: In hope I make myself more clear I give the exact commands I used with git's subtree strategy, which result in apparent loss of history of the files of the remote repository. Let A be the git repo I'm currently working in and B the git repo I'd like to incorporate into A as a subdirectory of it. It did the following:
编辑 2:希望我让自己更清楚,我给出了我在 git 的子树策略中使用的确切命令,这导致远程存储库文件的历史记录明显丢失。让 A 成为我目前正在使用的 git 存储库,而 B 是我想合并到 A 中作为它的子目录的 git 存储库。它做了以下事情:
git remote add -f B <url-of-B>
git merge -s ours --no-commit B/master
git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master
git commit -m "Merge B as subdirectory in subdir/Iwant/to/put/B/in."
After these commands and going into directory subdir/Iwant/to/put/B/in, I see all files of B, but git log
on any one of them shows just the commit message "Merge B as subdirectory in subdir/Iwant/to/put/B/in." Their file history as it is in B is lost.
在执行这些命令并进入目录 subdir/Iwant/to/put/B/in 后,我看到 B 的所有文件,但git log
在其中任何一个上只显示提交消息“将 B 合并为 subdir/Iwant/to/put 中的子目录/B/在。” 他们在 B 中的文件历史记录丢失了。
What seemsto work (since I'm a beginner on git I may be wrong) is the following:
什么似乎工作(因为我在混帐初学者我可能是错的)如下:
git remote add -f B <url-of-B>
git checkout -b B_branch B/master # make a local branch following B's master
git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&subdir/Iwant/to/put/B/in/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
git checkout master
git merge B_branch
The command above for filter-branch is taken from git help filter-branch
, in which I only changed the subdir path.
上面的 filter-branch 命令取自git help filter-branch
,其中我只更改了 subdir 路径。
采纳答案by Seth Robertson
After getting the fuller explanation of what is going on, I think I understand it and in any case at the bottom I have a workaround. Specifically, I believe what is happening is rename detection is being fooled by the subtree merge with --prefix. Here is my test case:
在对正在发生的事情有了更全面的解释后,我想我明白了,无论如何,在底部我有一个解决方法。具体来说,我相信正在发生的事情是重命名检测被带有 --prefix 的子树合并所愚弄。这是我的测试用例:
mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA
cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB
cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
git read-tree --prefix=bdir -u B/master
git commit -m "subtree merge B into bdir"
cd bdir
echo BBB>>B
git commit -a -m BBB
We make git directories a and b with several commits each. We do a subtree merge, and then we do a final commit in the new subtree.
我们创建 git 目录 a 和 b,每个目录都有多个提交。我们进行子树合并,然后在新的子树中进行最终提交。
Running gitk
(in z/a) shows that the history does appear, we can see it. Running git log
shows that the history does appear. However, looking at a specific file has a problem: git log bdir/B
运行gitk
(在 z/a 中)显示历史确实出现了,我们可以看到它。运行git log
表明历史确实出现了。但是,查看特定文件有一个问题: git log bdir/B
Well, there is a trick we can play. We can look at the pre-rename history of a specific file using --follow. git log --follow -- B
. This is good but isn't great since it fails to link the history of the pre-merge with the post-merge.
好吧,我们可以玩一个技巧。我们可以使用 --follow 查看特定文件的重命名前历史记录。 git log --follow -- B
. 这很好,但不是很好,因为它无法将合并前的历史与合并后的历史联系起来。
I tried playing with -M and -C, but I wasn't able to get it to follow one specific file.
我尝试使用 -M 和 -C,但我无法让它跟随一个特定的文件。
So, the solution, I feel, is to tell git about the rename that will be taking place as part of the subtree merge. Unfortunately git-read-tree is pretty fussy about subtree merges so we have to work through a temporary directory, but that can go away before we commit. Afterwards, we can see the full history.
所以,我觉得解决方案是告诉 git 将作为子树合并的一部分进行的重命名。不幸的是 git-read-tree 对子树合并非常挑剔,所以我们必须通过一个临时目录工作,但这可以在我们提交之前消失。之后,我们可以看到完整的历史。
First, create an "A" repository and make some commits:
首先,创建一个“A”存储库并进行一些提交:
mkdir -p z/a z/b
cd z/a
git init
echo A>A
git add A
git commit -m A
echo AA>>A
git commit -a -m AA
Second, create a "B" repository and make some commits:
其次,创建一个“B”存储库并进行一些提交:
cd ../b
git init
echo B>B
git add B
git commit -m B
echo BB>>B
git commit -a -m BB
And the trick to making this work: force Git to recognize the rename by creating a subdirectory and moving the contents into it.
以及完成这项工作的技巧:通过创建子目录并将内容移动到其中来强制 Git 识别重命名。
mkdir bdir
git mv B bdir
git commit -a -m bdir-rename
Return to repository "A" and fetch and merge the contents of "B":
返回存储库“A”并获取并合并“B”的内容:
cd ../a
git remote add -f B ../b
git merge -s ours --no-commit B/master
# According to Alex Brown and pjvandehaar, newer versions of git need --allow-unrelated-histories
# git merge -s ours --allow-unrelated-histories --no-commit B/master
git read-tree --prefix= -u B/master
git commit -m "subtree merge B into bdir"
To show that they're now merged:
为了表明它们现在已合并:
cd bdir
echo BBB>>B
git commit -a -m BBB
To prove the full history is preserved in a connected chain:
为了证明完整的历史记录保存在一个连接的链中:
git log --follow B
We get the history after doing this, but the problem is that if you are actually keeping the old "b" repo around and occasionally merging from it (say it is actually a third party separately maintained repo) you are in trouble since that third party will not have done the rename. You must try to merge new changes into your version of b with the rename and I fear that will not go smoothly. But if b is going away, you win.
这样做后我们得到了历史记录,但问题是,如果您实际上保留旧的“b”存储库并偶尔从中合并(假设它实际上是第三方单独维护的存储库),那么您就会遇到麻烦,因为该第三方不会进行重命名。您必须尝试通过重命名将新更改合并到您的 b 版本中,我担心这不会顺利进行。但是如果 b 消失了,你就赢了。
回答by kynan
git-subtree
is a script designed for exactly this use case of merging multiple repositories into one while preserving history (and/or splitting history of subtrees, though that is seems to be irrelevant to this question). It is distributed as part of the git tree since release 1.7.11.
git-subtree
是一个脚本,专为将多个存储库合并为一个同时保留历史记录(和/或拆分子树的历史记录,尽管这似乎与此问题无关)的用例而设计。自 1.7.11 版以来,它作为 git 树的一部分分发。
To merge a repository <repo>
at revision <rev>
as subdirectory <prefix>
, use git subtree add
as follows:
要将<repo>
修订版本中的存储库合并<rev>
为 subdirectory <prefix>
,请使用git subtree add
如下:
git subtree add -P <prefix> <repo> <rev>
git-subtree implements the subtree merge strategyin a more user friendly manner.
git-subtree以更加用户友好的方式实现了子树合并策略。
The downsideis that in the merged history the files are unprefixed (not in a subdirectory). Say you merge repository a
into b
. As a result git log a/f1
will show you all the changes (if any) except those in the merged history. You can do:
的缺点是,在合并后的历史文件前缀的(不是在子目录中)。假设您将存储库合并a
到b
. 结果git log a/f1
将向您显示除合并历史记录中的更改之外的所有更改(如果有)。你可以做:
git log --follow -- f1
but that won't show the changes other then in the merged history.
但这不会显示合并历史记录中的其他更改。
In other words, if you don't change a
's files in repository b
, then you need to specify --follow
and an unprefixed path. If you change them in both repositories, then you have 2 commands, none of which shows all the changes.
换句话说,如果您不更改a
repository 中的文件b
,则需要指定--follow
一个不带前缀的路径。如果您在两个存储库中更改它们,那么您有 2 个命令,其中没有一个显示所有更改。
More on it here.
更多关于它在这里。
回答by hfs
I wanted to
我想
- keep a linear history without explicit merge, and
- make it look like the files of the merged repository had always existed in the subdirectory, and as a side effect make
git log -- file
work without--follow
.
- 在没有显式合并的情况下保持线性历史,并且
- 使它看起来像合并存储库的文件一直存在于子目录中,并且作为副作用使
git log -- file
工作没有--follow
.
Step 1: Rewrite history in the source repository to make it look like all files always existed below the subdirectory.
第 1 步:重写源存储库中的历史记录,使其看起来所有文件始终存在于子目录下。
Create a temporary branch for the rewritten history.
为重写的历史创建一个临时分支。
git checkout -b tmp_subdir
Then use git filter-branch
as described in How can I rewrite history so that all files, except the ones I already moved, are in a subdirectory?:
然后git filter-branch
按照如何重写历史记录中的说明使用,以便除我已经移动的文件之外的所有文件都在子目录中?:
git filter-branch --prune-empty --tree-filter '
if [ ! -e foo/bar ]; then
mkdir -p foo/bar
git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files foo/bar
fi'
Step 2: Switch to the target repository. Add the source repository as remote in the target repository and fetch its contents.
第 2 步:切换到目标存储库。将源存储库作为远程存储库添加到目标存储库中并获取其内容。
git remote add sourcerepo .../path/to/sourcerepo
git fetch sourcerepo
Step 3: Use merge --onto
to add the commits of the rewritten source repository on top of the target repository.
第 3 步:merge --onto
用于将重写的源存储库的提交添加到目标存储库之上。
git rebase --preserve-merges --onto master --root sourcerepo/tmp_subdir
You can check the log to see that this really got you what you wanted.
您可以检查日志以查看这是否确实满足了您的需求。
git log --stat
Step 4: After the rebase you're in “detached HEAD” state. You can fast-forward master to the new head.
第 4 步:在 rebase 之后,您处于“分离的 HEAD”状态。您可以快进掌握到新的头部。
git checkout -b tmp_merged
git checkout master
git merge tmp_merged
git branch -d tmp_merged
Step 5: Finally some cleanup: Remove the temporary remote.
第 5 步:最后一些清理工作:删除临时遥控器。
git remote rm sourcerepo
回答by Adam Dymitruk
If you are really wanting to stitch things together, look up grafting. You should also be using git rebase --preserve-merges --onto
. There is also an option to keep the author date for the committer information.
如果你真的想把东西缝合在一起,看看嫁接。您还应该使用git rebase --preserve-merges --onto
. 还有一个选项可以保留提交者信息的作者日期。
回答by 0__
I found the following solution workable for me. First I go into project B, create a new branch in which already all files will be moved to the new sub directory. I then push this new branch to origin. Next I go to project A, add and fetch the remote of B, then I checkout the moved branch, I go back into master and merge:
我发现以下解决方案对我有用。首先,我进入项目 B,创建一个新分支,其中已经将所有文件移动到新的子目录。然后我将这个新分支推送到原点。接下来,我转到项目 A,添加并获取 B 的遥控器,然后检出移动的分支,返回 master 并合并:
#?in local copy of project B
git checkout -b prepare_move
mkdir subdir
git mv <files_to_move> subdir/
git commit -m 'move files to subdir'
git push origin prepare_move
# in local copy of project A
git remote add -f B_origin <remote-url>
git checkout -b from_B B_origin/prepare_move
git checkout master
git merge from_B
If I go to sub directory subdir
, I can use git log --follow
and still have the history.
如果我转到子目录subdir
,我可以使用git log --follow
并且仍然有历史记录。
I'm not a git expert, so I cannot comment whether this is a particularly good solution or if it has caveats, but so far it seems all fine.
我不是 git 专家,所以我无法评论这是否是一个特别好的解决方案,或者它是否有警告,但到目前为止似乎一切都很好。
回答by Abizern
Have you tried adding the extra repository as a git submodule? It won't merge the history with the containing repository, in fact, it will be an independent repository.
您是否尝试将额外的存储库添加为 git 子模块?它不会将历史记录与包含的存储库合并,实际上,它将是一个独立的存储库。
I mention it, because you haven't.
我提到它,因为你没有。
回答by x-yuri
Say you want to merge repository a
into b
(I'm assuming they're located alongside one another):
假设您想将存储库合并a
到b
(我假设它们并排放置):
cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a
For this you need git-filter-repo
installed (filter-branch
is discouraged).
为此,您需要git-filter-repo
安装(filter-branch
被劝阻)。
An example of merging 2 big repositories, putting one of them into a subdirectory: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
合并 2 个大型存储库,将其中一个放入子目录的示例:https: //gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
More on it here.
更多关于它在这里。