如何将现有的 Git 存储库导入另一个?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1683531/
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
How to import existing Git repository into another?
提问by Vijay Patel
I have a Git repository in a folder called XXX, and I have second Git repository called YYY.
我在名为XXX的文件夹中有一个 Git 存储库,我有第二个名为YYY 的Git 存储库。
I want to import the XXXrepository into the YYYrepository as a subdirectory named ZZZand add all XXX's change history to YYY.
我想将XXX存储库作为名为ZZZ的子目录导入YYY存储库,并将所有XXX的更改历史记录添加到YYY。
Folder structure before:
之前的文件夹结构:
├── XXX
│ ├── .git
│ └── (project files)
└── YYY
├── .git
└── (project files)
Folder structure after:
之后的文件夹结构:
YYY
├── .git <-- This now contains the change history from XXX
├── ZZZ <-- This was originally XXX
│ └── (project files)
└── (project files)
Can this be done, or must I resort to using sub-modules?
这可以做到吗,还是我必须求助于使用子模块?
采纳答案by ebneter
Probably the simplest way would be to pull the XXXstuff into a branch in YYYand then merge it into master:
可能最简单的方法是将XXX内容拉入YYY 中的一个分支,然后将其合并到 master 中:
In YYY:
在YYY 中:
git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master
git merge ZZZ --allow-unrelated-histories # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ # to get rid of the extra branch before pushing
git push # if you have a remote, that is
I actually just tried this with a couple of my repos and it works. Unlike J?rg's answerit won't let you continue to use the other repo, but I don't think you specified that anyway.
我实际上只是用我的几个 repos 尝试过这个,它有效。与J?rg 的回答不同,它不会让您继续使用其他存储库,但我认为您无论如何都没有指定。
Note: Since this was originally written in 2009, git has added the subtree merge mentioned in the answer below. I would probably use that method today, although of course this method does still work.
注意:由于这最初是在 2009 年编写的,因此 git 添加了下面答案中提到的子树合并。我今天可能会使用这种方法,当然这种方法仍然有效。
回答by ColinM
If you want to retain the exact commit history of the second repository and therefore also retain the ability to easily merge upstream changes in the future then here is the method you want. It results in unmodified history of the subtree being imported into your repo plus one merge commit to move the merged repository to the subdirectory.
如果您想保留第二个存储库的确切提交历史,并因此也保留将来轻松合并上游更改的能力,那么这就是您想要的方法。这会导致子树的未修改历史记录被导入到您的存储库中,再加上一次合并提交以将合并的存储库移动到子目录。
git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."
You can track upstream changes like so:
您可以像这样跟踪上游更改:
git pull -s subtree XXX_remote master
Git figures out on its own where the roots are before doing the merge, so you don't need to specify the prefix on subsequent merges.
Git 会在进行合并之前自行确定根的位置,因此您无需在后续合并时指定前缀。
The downsideis that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a
will show you all the changes (if any) except those in the merged history. You can do:
的缺点是,在合并后的历史文件前缀的(不是在子目录中)。结果git log ZZZ/a
将向您显示除合并历史记录中的更改之外的所有更改(如果有)。你可以做:
git log --follow -- a
but that won't show the changes other then in the merged history.
但这不会显示合并历史记录中的其他更改。
In other words, if you don't change ZZZ
's files in repository XXX
, 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.
换句话说,如果您不更改ZZZ
repository 中的文件XXX
,则需要指定--follow
一个不带前缀的路径。如果您在两个存储库中更改它们,那么您有 2 个命令,其中没有一个显示所有更改。
Git versions before 2.9: You don't need to pass the --allow-unrelated-histories
option to git merge
.
2.9 之前的 Git 版本:您不需要将--allow-unrelated-histories
选项传递给git merge
.
The method in the other answer that uses read-tree
and skips the merge -s ours
step is effectively no different than copying the files with cp and committing the result.
使用read-tree
并跳过该merge -s ours
步骤的另一个答案中的方法实际上与使用 cp 复制文件并提交结果没有什么不同。
Original source was from github's "Subtree Merge" help article. And another useful link.
原始来源来自github 的“子树合并”帮助文章。而另一种有用的链接。
回答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 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以更加用户友好的方式实现了子树合并策略。
For your case, inside repository YYY, you would run:
对于您的情况,在存储库 YYY 中,您将运行:
git subtree add -P ZZZ /path/to/XXX.git master
The downsideis that in the merged history the files are unprefixed (not in a subdirectory). As a result git log ZZZ/a
will show you all the changes (if any) except those in the merged history. You can do:
的缺点是,在合并后的历史文件前缀的(不是在子目录中)。结果git log ZZZ/a
将向您显示除合并历史记录中的更改之外的所有更改(如果有)。你可以做:
git log --follow -- a
but that won't show the changes other then in the merged history.
但这不会显示合并历史记录中的其他更改。
In other words, if you don't change ZZZ
's files in repository XXX
, 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.
换句话说,如果您不更改ZZZ
repository 中的文件XXX
,则需要指定--follow
一个不带前缀的路径。如果您在两个存储库中更改它们,那么您有 2 个命令,其中没有一个显示所有更改。
More on it here.
更多关于它在这里。
回答by J?rg W Mittag
There is a well-known instance of this in the Git repository itself, which is collectively known in the Git community as "the coolest merge ever" (after the subject line Linus Torvalds used in the e-mail to the Git mailinglist which describes this merge). In this case, the gitk
Git GUI which now is part of Git proper, actually used to be a separate project. Linus managed to merge that repository into the Git repository in a way that
在 Git 存储库本身中有一个众所周知的实例,它在 Git 社区中被统称为“有史以来最酷的合并”(在发送给 Git 邮件列表的电子邮件中使用的主题行 Linus Torvalds 之后,它描述了这一点合并)。在这种情况下,gitk
现在属于 Git 的一部分的Git GUI 实际上曾经是一个单独的项目。Linus 设法以一种方式将该存储库合并到 Git 存储库中
- it appears in the Git repository as if it had always been developed as part of Git,
- all the history is kept intact and
- it can still be developed independently in its old repository, with changes simply being
git pull
ed.
- 它出现在 Git 存储库中,就好像它一直是作为 Git 的一部分开发的一样,
- 所有的历史都完好无损
- 它仍然可以在其旧存储库中独立开发,只需进行更改即可
git pull
。
The e-mail contains the steps needed to reproduce, but it is not for the faint of heart: first, Linus wroteGit, so he probably knows a bit more about it than you or me, and second, this was almost 5 years ago and Git has improved considerablysince then, so maybe it is now much easier.
该电子邮件包含复制所需的步骤,但它不适合胆小的人:首先,Linus编写了Git,所以他可能比你或我更了解它,其次,这几乎是 5 年前和Git有所改善显着从那以后,也许现在是很容易。
In particular, I guess nowadays one would use a gitk submodule, in that specific case.
特别是,我猜现在人们会在那种特定情况下使用 gitk 子模块。
回答by Damien R.
The simple way to do that is to use git format-patch.
做到这一点的简单方法是使用 git format-patch。
Assume we have 2 git repositories fooand bar.
假设我们有 2 个 git 存储库foo和bar。
foocontains:
foo包含:
- foo.txt
- .git
- foo.txt
- .git
barcontains:
酒吧包含:
- bar.txt
- .git
- 栏.txt
- .git
and we want to end-up with foocontaining the barhistory and these files:
并且我们希望以包含条形历史记录和这些文件的foo结束:
- foo.txt
- .git
- foobar/bar.txt
- foo.txt
- .git
- foobar/bar.txt
So to do that:
所以要做到这一点:
1. create a temporary directory eg PATH_YOU_WANT/patch-bar
2. go in bar directory
3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
4. go in foo directory
5. git am PATH_YOU_WANT/patch-bar/*
And if we want to rewrite all message commits from bar we can do, eg on Linux:
如果我们想重写 bar 中的所有消息提交,我们可以这样做,例如在 Linux 上:
git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD
This will add "[bar] " at the beginning of each commit message.
这将在每个提交消息的开头添加“[bar]”。
回答by Andrey Izman
This function will clone remote repo into local repo dir, after merging all commits will be saved, git log
will be show the original commits and proper paths:
此函数将远程仓库克隆到本地仓库目录,合并后所有提交将被保存,git log
将显示原始提交和正确路径:
function git-add-repo
{
repo=""
dir="$(echo "" | sed 's/\/$//')"
path="$(pwd)"
tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"
git clone "$repo" "$tmp"
cd "$tmp"
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"$dir"'/," |
GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD
cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"
}
How to use:
如何使用:
cd current/package
git-add-repo https://github.com/example/example dir/to/save
If make a little changes you can even move files/dirs of merged repo into different paths, for example:
如果稍作更改,您甚至可以将合并的 repo 的文件/目录移动到不同的路径中,例如:
repo="https://github.com/example/example"
path="$(pwd)"
tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"
git clone "$repo" "$tmp"
cd "$tmp"
GIT_ADD_STORED=""
function git-mv-store
{
from="$(echo "" | sed 's/\./\./')"
to="$(echo "" | sed 's/\./\./')"
GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}
# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'
git filter-branch --index-filter '
git ls-files -s |
sed "'"$GIT_ADD_STORED"'" |
GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD
GIT_ADD_STORED=""
cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"
Notices
Paths replaces via sed
, so make sure it moved in proper paths after merging.
The --allow-unrelated-histories
parameter only exists since git >= 2.9.
注意
Paths 替换了 via sed
,因此请确保合并后它在正确的路径中移动。
该--allow-unrelated-histories
参数仅在 git >= 2.9 后才存在。
回答by Alex
Based on this article, using subtree is what worked for me and only applicable history was transferred. Posting here in case anyone needs the steps (make sure to replace the placeholders with values applicable to you):
基于这篇文章,使用子树对我有用,并且只传输了适用的历史记录。如果有人需要这些步骤,请在此处发布(确保将占位符替换为适用于您的值):
in your source repository split subfolder into a new branch
在您的源存储库中将子文件夹拆分为一个新分支
git subtree split --prefix=<source-path-to-merge> -b subtree-split-result
git subtree split --prefix=<source-path-to-merge> -b subtree-split-result
in your destination repo merge in the split result branch
在您的目标回购中合并拆分结果分支
git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result
verify your changes and commit
验证您的更改并提交
git status
git commit
Don't forget to
不要忘记
Clean up by deleting the subtree-split-result
branch
通过删除subtree-split-result
分支进行清理
git branch -D subtree-split-result
git branch -D subtree-split-result
Remove the remote you added to fetch the data from source repo
删除您添加的遥控器以从源存储库中获取数据
git remote rm merge-source-repo
git remote rm merge-source-repo
回答by gaoithe
Adding another answer as I think this is a bit simpler. A pull of repo_dest is done into repo_to_import and then a push --set-upstream url:repo_dest master is done.
添加另一个答案,因为我认为这更简单一些。将 repo_dest 拉到 repo_to_import 中,然后推送 --set-upstream url:repo_dest master 完成。
This method has worked for me importing several smaller repos into a bigger one.
这种方法对我有用,可以将几个较小的存储库导入一个更大的存储库。
How to import: repo1_to_import to repo_dest
如何导入:repo1_to_import 到 repo_dest
# checkout your repo1_to_import if you don't have it already
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import
# now. pull all of repo_dest
git pull url:repo_dest
ls
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master
# repeat for other repositories you want to import
Rename or move files and dirs into desired position in original repo before you do the import. e.g.
在执行导入之前,将文件和目录重命名或移动到原始存储库中的所需位置。例如
cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import
The method described at the following link inspired this answer. I liked it as it seemed more simple. BUT Beware! There be dragons! https://help.github.com/articles/importing-an-external-git-repositorygit push --mirror url:repo_dest
pushes your local repo history and state to remote (url:repo_dest). BUT it deletes the old history and state of the remote. Fun ensues! :-E
以下链接中描述的方法启发了这个答案。我喜欢它,因为它看起来更简单。但要小心!有龙!https://help.github.com/articles/importing-an-external-git-repositorygit push --mirror url:repo_dest
将你的本地仓库历史和状态推送到远程(url:repo_dest)。但它会删除遥控器的旧历史和状态。乐趣随之而来!:-E
回答by VeLKerr
See Basic examplein this articleand consider such mapping on repositories:
请参阅基本示例在这篇文章中,并考虑对库这样的映射:
A
<->YYY
,B
<->XXX
A
<->YYY
,B
<->XXX
After all activity described in this chapter (after merging), remove branch B-master
:
在本章中描述的所有活动之后(合并后),删除分支B-master
:
$ git branch -d B-master
Then, push changes.
然后,推送更改。
It works for me.
这个对我有用。
回答by Sebastian Blask
I wanted to import only some files from the other repository (XXX) in my case. The subtree was too complicated for me and the other solutions didn't work. This is what I did:
在我的情况下,我只想从其他存储库 (XXX) 导入一些文件。子树对我来说太复杂了,其他解决方案不起作用。这就是我所做的:
ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')
This gives you a space-separated list of all the commits that affect the files I wanted to import (ZZZ) in reverse order (you might have to add --follow to capture renames as well). I then went into the target repository (YYY), added the other repository (XXX) as remote, did a fetch from it and finally:
这为您提供了一个以空格分隔的所有提交列表,这些提交会以相反的顺序影响我想要导入的文件 (ZZZ)(您可能还必须添加 --follow 以捕获重命名)。然后我进入目标存储库(YYY),将另一个存储库(XXX)添加为远程存储库,从中获取数据,最后:
git cherry-pick $ALL_COMMITS
which adds all the commits to your branch, you'll thus have all the files with their history and can do whatever you want with them as if they've always been in this repository.
它将所有提交添加到您的分支,因此您将拥有所有文件及其历史记录,并且可以对它们做任何您想做的事情,就好像它们一直在这个存储库中一样。