如何将现有Git存储库导入另一个?
我在一个名为XXX的文件夹中有一个Git存储库,第二个Git存储库名为YYY 我想将XXX存储库作为名为ZZZ的子目录导入YYY存储库,并将所有XXX的更改历史添加到YYY 之前的文件夹结构:如何将现有Git存储库导入另一个?,git,merge,git-merge,Git,Merge,Git Merge,我在一个名为XXX的文件夹中有一个Git存储库,第二个Git存储库名为YYY 我想将XXX存储库作为名为ZZZ的子目录导入YYY存储库,并将所有XXX的更改历史添加到YYY 之前的文件夹结构: ├── XXX │ ├── .git │ └── (project files) └── YYY ├── .git └── (project files) 文件夹结构之后: YYY ├── .git <-- This now contains the change his
├── XXX
│ ├── .git
│ └── (project files)
└── YYY
├── .git
└── (project files)
文件夹结构之后:
YYY
├── .git <-- This now contains the change history from XXX
├── ZZZ <-- This was originally XXX
│ └── (project files)
└── (project files)
YYY
├── .git我认为您可以使用“git mv”和“git pull”来实现这一点
我是一个普通的GitNoob——所以要小心你的主存储库——但我只是在一个临时目录中尝试了一下,它似乎起了作用
首先-重命名XXX的结构,以匹配在YYY内时的外观:
cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ
现在XXX看起来像这样:
XXX
|- ZZZ
|- ZZZ
YYY
|- ZZZ
|- ZZZ
|- (other folders that already were in YYY)
现在使用“git pull”获取跨以下对象的更改:
cd ../YYY
git pull ../XXX
现在YYY看起来像这样:
XXX
|- ZZZ
|- ZZZ
YYY
|- ZZZ
|- ZZZ
|- (other folders that already were in YYY)
我不知道有什么简单的方法可以做到这一点。您可以这样做:
使用git筛选器分支在XXX存储库中添加ZZZ超级目录
将新分支推送到YYY存储库
将推送的分支与YYY的主干合并
如果这听起来很吸引人,我可以编辑细节。在Git存储库中有一个著名的例子,在Git社区中统称为“”(在发送给Git mailinglist的电子邮件中使用的主题行Linus Torvalds之后,该邮件列表描述了此合并)。在本例中,gitk
gitgui现在是Git本身的一部分,实际上是一个单独的项目。Linus以一种
- 它出现在Git存储库中,就好像它一直是作为Git的一部分开发的一样
- 所有的历史都完好无损
- 它仍然可以在其旧存储库中独立开发,只需对其进行
git-pull
ed更改即可
这封电子邮件包含了复制所需的步骤,但它不是为胆小的人准备的:首先,Linus写了Git,所以他可能比你或我更了解Git,其次,这是近5年前的事了,Git从那时起有了很大的改进,所以现在可能更容易了
特别是,我想现在人们会使用gitk子模块,在这种特定情况下。最简单的方法可能是将XXX内容拉入YYY中的一个分支,然后将其合并到master中:
在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
事实上,我只是用我的几份回购协议尝试了一下,效果不错。不同的是,它不会让你继续使用另一种回购协议,但我认为你无论如何都不会指定
注意:由于这最初是在2009年编写的,git添加了下面答案中提到的子树合并。我今天可能会使用这种方法,当然这种方法仍然有效。简单的方法是使用git格式的补丁
假设我们有两个git存储库foo和bar
foo包含:
- foo.txt
- 吉特先生
栏包含:
- bar.txt
- 吉特先生
我们希望以foo结束,其中包含条历史记录和以下文件:
- foo.txt
- 吉特先生
- foobar/bar.txt
为此:
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/*
如果我们想重写bar中的所有消息提交,我们可以这样做,例如在Linux上:
git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD
这将在每个提交消息的开头添加“[bar]”。如果您希望保留第二个存储库的确切提交历史记录,从而也能够在将来轻松合并上游更改,那么下面是您想要的方法。它会导致导入到repo中的子树的未修改历史加上一个合并提交,以将合并的存储库移动到子目录
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."
Git在进行合并之前会自行确定根的位置,因此不需要在后续合并中指定前缀
缺点是,在合并的历史记录中,文件是不固定的(不在子目录中)。因此,git log ZZZ/a
将显示除合并历史记录中的更改以外的所有更改(如果有)。你可以做:
git log --follow -- a
git log --follow -- a
但这不会显示合并历史中的其他更改
换句话说,如果不更改存储库中的ZZZ
文件XXX
,则需要指定--follow
和一个不固定的路径。如果在两个存储库中都更改了它们,则有两个命令,其中没有一个显示所有更改
2.9之前的Git版本:您不需要将--允许不相关的历史记录
选项传递到Git合并
另一个答案中使用readtree
并跳过merge-sours
步骤的方法实际上与使用cp复制文件并提交结果没有什么不同
原来的来源是从。而且。在我的例子中,我只想从另一个存储库(XXX)导入一些文件。子树对我来说太复杂了,其他的解决方案都不起作用。这就是我所做的:
ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')
这将为您提供一个以空格分隔的列表,其中列出了影响我要导入的文件(ZZZ)的所有提交(您可能还需要添加--follow以捕获重命名)。然后,我进入目标存储库(YYY),将另一个存储库(XXX)添加为远程存储库,从中提取数据,最后:
git cherry-pick $ALL_COMMITS
这会将所有提交添加到您的分支中,因此您将拥有所有文件及其历史记录,并且可以对它们执行任何您想要的操作,就好像它们一直在这个存储库中一样。添加另一个答案,因为我认为这更简单一些。将repo_dest拉入repo_to_import,然后推式设置上游url:repo_dest master
这种方法对我来说很有效,它将几个较小的回购协议导入到一个较大的回购协议中
如何导入:repo1\u到repo\u导入到repo\u dest
# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master # create new master from upstream/master
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
git status
git commit
git subtree add -P <prefix> <repo> <rev>
git subtree add -P ZZZ /path/to/XXX.git master
git log --follow -- a
$ git branch -d B-master
function git-add-repo
{
repo="$1"
dir="$(echo "$2" | 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"
}
cd current/package
git-add-repo https://github.com/example/example dir/to/save
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 "$1" | sed 's/\./\\./')"
to="$(echo "$2" | 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"
# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master
# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master
# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master
# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link
# The same result with a single command
gil update
gil commit -a -m "Some big update"
gil pull
gil push
usage: gil command arguments
Supported commands:
help - show this help
context - command will show the current git link context of the current directory
clone - clone all repositories that are missed in the current context
link - link all repositories that are missed in the current context
update - clone and link in a single operation
pull - pull all repositories in the current directory
push - push all repositories in the current directory
commit - commit all repositories in the current directory
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
cd tempDir
git clone <location of repo to be merged> --no-local