我在一个名为XXX的文件夹中有一个Git存储库,还有一个名为YYY的Git存储库。

我想将XXX存储库作为名为ZZZ的子目录导入到YYY存储库中,并将所有XXX的更改历史添加到YYY中。

之前的文件夹结构:

├── 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)

这可以做到吗,或者我必须使用子模块?


当前回答

下面是脚本,将立即工作。

#!/bin/bash -xe
# script name: merge-repo.sh
# To merge repositories into the current.
# To see the log of the new repo use 'git log --follow -- unprefixed-filename'
# So if the file is repo/test.cpp use 'git log --follow -- test.cpp'
# I'm not sure how this will work when two files have the same name.
#
# `git branch -a` will show newly created branches.
# You can delete them if you want.
merge_another() {
    repo="$1" # url of the remote repo
    rn="$2"   # new name of the repo, you can keep the same name as well.
    git remote add ${rn} ${repo}
    git fetch ${rn}
    git merge -s ours --no-commit --allow-unrelated-histories ${rn}/master
    git read-tree --prefix=${rn}/ -u ${rn}/master
    git commit -m "Imported ${rn} as a subtree."
    git pull -s subtree ${rn} master
}

merge_another $1 $2

运行脚本。转到您希望合并另一个repo的repo,并运行脚本。

cd base-repo
./merge-repo.sh git@github.com:username/repo-to-be-merged.git repo-to-be-merged-new-name

现在将主分支上的更改推到remote/origin。根据您要做的事情,可能不需要此步骤。

git push origin master

其他回答

可能最简单的方法是将XXX的东西拉到YYY的分支中,然后合并到master中:

多:

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

实际上,我刚刚用我的几个回购尝试了这个,它是有效的。不像Jörg的答案,它不会让你继续使用另一个回购,但我不认为你指定无论如何。

注意:由于这篇文章最初写于2009年,git添加了下面答案中提到的子树合并。我今天可能会用这个方法,当然这个方法仍然有效。

Git-subtree正是为这样的用例设计的脚本:将多个存储库合并为一个,同时保留历史(和/或分割子树的历史,尽管这似乎与这个问题无关)。从1.7.11版开始,它作为git树的一部分发布。

要合并一个<repo> at revision <rev> as subdirectory <prefix>的仓库,使用git子树add,如下所示:

git subtree add -P <prefix> <repo> <rev>

Git-subtree以一种更用户友好的方式实现子树合并策略。

对于您的情况,在存储库YYY中,您将运行:

git subtree add -P ZZZ /path/to/XXX.git master

缺点是在合并的历史中文件没有前缀(不在子目录中)。因此,git log ZZZ/a会显示除了合并历史之外的所有更改(如果有的话)。你可以:

git log --follow -- a

但这不会显示合并历史中其他的变化。

换句话说,如果不更改存储库XXX中的ZZZ文件,则需要指定——follow和一个无前缀路径。如果在两个存储库中都更改它们,则有两个命令,其中没有一个显示所有更改。

这里有更多。

没有足够的代表给x-yuri的回答加上评论,但它工作得很漂亮,保存了历史。 我正在与两个工作的本地回购工作,并收到这个错误:

中止:拒绝破坏性地覆盖此后的回购历史 这看起来不像一个新的克隆体。 (预计新包装的回购) 请改用一个新的克隆体做手术。如果你想继续,那就用武力。

与其担心——force标志的含义,我先在本地克隆了这个repo:

cd tempDir
git clone <location of repo to be merged> --no-local

用这个新克隆的拷贝来执行x-尤里布置的一系列命令。 最后,在:git filter-repo——to-subdirectory-filter a中,a是你要导入的repo根文件夹的名称。

我认为你可以使用'git mv'和'git pull'来做到这一点。

我是一个公平的git新手-所以要小心你的主存储库-但我刚刚在一个临时目录中尝试了这一点,它似乎工作。

首先-重命名XXX的结构,以匹配你想要它在YYY中的样子:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

现在XXX是这样的:

XXX
 |- ZZZ
     |- ZZZ

现在使用'git pull'来获取更改:

cd ../YYY
git pull ../XXX

现在YYY是这样的:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)

添加另一个答案,因为我认为这有点简单。将repo_dest拉入到repo_to_import中,然后推入——set-upstream url:repo_dest master。

这种方法对我来说很有效,我把几个较小的回购导入一个较大的回购中。

如何将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

重命名或移动文件和dirs到原始回购所需的位置,然后再进行导入。如。

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

以下链接中描述的方法启发了这个答案。我喜欢它,因为它看起来更简单。但是要小心!有龙!https://help.github.com/articles/importing-an-external-git-repository git push——镜像url:repo_dest将本地的回购历史和状态推送到远程(url:repo_dest)。但是它会删除旧的历史记录和远程状态。乐趣随之而来!: - e