如何将现有的Git存储库导入另一个?

Vij*_*tel 445 git merge git-merge

我在一个名为XXX的文件夹中有一个Git存储库,我有第二个名为YYY的 Git存储库.

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

文件夹结构之前:

??? XXX
?   ??? .git
?   ??? (project files)
??? YYY
    ??? .git
    ??? (project files)
Run Code Online (Sandbox Code Playgroud)

文件夹结构后:

YYY
??? .git  <-- This now contains the change history from XXX
???  ZZZ  <-- This was originally XXX
?    ??? (project files)
???  (project files)
Run Code Online (Sandbox Code Playgroud)

可以这样做,还是我必须使用子模块?

ebn*_*ter 407

可能最简单的方法是将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
Run Code Online (Sandbox Code Playgroud)

我实际上只是尝试了几个我的回购,它的工作原理.与Jörg的回答不同,它不会让你继续使用其他回购,但我认为你无论如何都不会指定.

注意:由于这最初是在2009年编写的,因此git添加了下面答案中提到的子树合并.我今天可能会使用这种方法,虽然这种方法当然仍然可行.

  • @SebastianBlask我刚刚用我的两个回购品弄乱了这件事,并意识到尽管我多年来一直在投票,但似乎没有人注意到这一步.:-)我_ submitted_将它合并为master,但实际上没有显示出来.立即编辑... (4认同)
  • 谢谢。我使用了您的技术的稍微修改版本:我在 XXX 上创建了一个“staging”分支,在其中创建了 ZZZ 文件夹,并将“stuff”移动到其中。然后我将XXX合并到YYY中。 (2认同)
  • 你可以在将文件移动到子文件夹时添加这样的东西:`git mv $(ls | grep -v <你的foldername>)<你的foldername> /`这会将所有文件和文件夹复制到你的新文件夹中 (2认同)

Col*_*inM 359

如果您想保留第二个存储库的确切提交历史记录,因此也保留了将来轻松合并上游更改的功能,那么这就是您想要的方法.它导致子树的未修改历史记录被导入到您的仓库中,加上一个合并提交将合并的存储库移动到子目录.

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."
Run Code Online (Sandbox Code Playgroud)

您可以跟踪上游更改,如下所示:

git pull -s subtree XXX_remote master
Run Code Online (Sandbox Code Playgroud)

Git在进行合并之前会根据其自身的位置计算出来,因此您无需在后续合并中指定前缀.

2.9之前的Git版本:您不需要将--allow-unrelated-histories选项传递给git merge.

使用read-tree和跳过该merge -s ours步骤的另一个答案中的方法实际上与使用cp复制文件并提交结果没有什么不同.

原始资料来自github的"Subtree Merge"帮助文章.

  • 这似乎没有保留历史记录...如果我在我输入的任何文件上执行`git log`我只看到单个合并提交,而在其他回购中没有任何内容?Git 1.8.0 (8认同)
  • 啊哈!如果我使用导入文件的旧路径,即省略它已被导入的子目录,那么git log将给我提交历史记录,例如`git log - myfile`而不是`git log - rack/myfile` (8认同)
  • 从Git 2.9开始,在进行合并时需要选择`--allow-unrelated-histories`. (7认同)
  • @FrancescoFrassinelli,如果你不想要历史,为什么不做一个普通的副本呢?我试图弄清楚如果不是历史记录会让你对这种方法有什么吸引力 - 这是我使用这种方法的唯一原因! (4认同)
  • @FrancescoFrassinelli,不是那么可取吗?引入历史是这种方法的*特征*. (2认同)
  • @FrancescoFrassinelli您是否尝试过"git log"的"--follow"选项? (2认同)

kyn*_*nan 81

git-subtree是一个脚本,专门用于将多个存储库合并为一个同时保留历史记录(和/或拆分子树历史,尽管这似乎与此问题无关)的用例.它自1.7.11发布以来作为git树的一部分进行分发.

要将<repo>版本的存储库合并<rev>为子目录<prefix>,请使用git subtree add以下命令:

git subtree add -P <prefix> <repo> <rev>
Run Code Online (Sandbox Code Playgroud)

git-subtree 以更加用户友好的方式实现子树合并策略.

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

git subtree add -P ZZZ /path/to/XXX.git master
Run Code Online (Sandbox Code Playgroud)

  • 如果你有一个目录要合并而不是裸存储库或远程,`git subree add -P name-of-desired-prefix~/location/of/git/repo-without-.git branch-name` (4认同)
  • Noob经验:git(版本2.9.0.windows.1)响应"致命:模糊的参数'HEAD':未知的修订或路径不在工作树中"当我在一个刚刚初始化的本地非裸存储库中尝试这个时,但是在我_really_获得新的存储库之后,即在添加普通文件并以常规方式提交后,它工作正常. (2认同)

Jör*_*tag 48

在Git存储库本身就有一个众所周知的例子,它在Git社区中被统称为" 有史以来最酷的合并 "(在主题行Linus Torvalds用于电子邮件中的Git邮件列表中描述了这一点)合并).在这种情况下,gitkGit GUI现在是Git的一部分,实际上曾经是一个单独的项目.Linus设法以一种方式将该存储库合并到Git存储库中

  • 它出现在Git存储库中,好像它一直是作为Git的一部分开发的,
  • 所有的历史都保持完整
  • 它仍然可以在旧的存储库中独立开发,只需进行更改即可git pull.

电子邮件包含了重现所需的步骤,但它不适合胆小的人:首先,Linus 写了 Git,所以他可能比你或我更了解它,其次,这是差不多5年前从那时起,Git已经有了很大的改进,所以现在可能更容易了.

特别是,我想现在有人会在特定情况下使用gitk子模块.

  • BTW.用于后续合并的策略(如果有的话)被称为**子树**合并,并且有第三方`git-subtree`工具可以帮助你解决这个问题:http://github.com/apenwarr/git -subtree (3认同)

Dam*_* R. 12

这样做的简单方法是使用git format-patch.

假设我们有2个git存储库foobar.

foo包含:

  • foo.txt的
  • git的

包含:

  • 跳回到bar.txt
  • git的

我们希望最终得到包含条形历史和这些文件的foo:

  • foo.txt的
  • git的
  • foob​​ar的/跳回到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/*
Run Code Online (Sandbox Code Playgroud)

如果我们想要重写bar中的所有消息提交,我们可以做,例如在Linux上:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD
Run Code Online (Sandbox Code Playgroud)

这将在每个提交消息的开头添加"[bar]".


Ale*_*lex 7

基于这篇文章,使用子树对我有用,只转移了适用的历史记录.如果有人需要这些步骤(请确保用适用于您的值替换占位符),请在此处发布:

在源存储库中将子文件夹拆分为新分支

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

在您的目标repo合并拆分结果分支

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
Run Code Online (Sandbox Code Playgroud)

验证您的更改并提交

git status
git commit
Run Code Online (Sandbox Code Playgroud)

别忘了

通过删除subtree-split-result分支进行清理

git branch -D subtree-split-result

删除您添加的远程数据库以从源存储库获取数据

git remote rm merge-source-repo


And*_*man 7

此函数将远程repo克隆到本地repo dir,合并后将保存所有提交,git log将显示原始提交和正确的路径:

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"
}
Run Code Online (Sandbox Code Playgroud)

如何使用:

cd current/package
git-add-repo https://github.com/example/example dir/to/save
Run Code Online (Sandbox Code Playgroud)

如果进行一些更改,您甚至可以将合并仓库的文件/目录移动到不同的路径中,例如:

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"
Run Code Online (Sandbox Code Playgroud)

注意
路径替换via sed,因此请确保在合并后将其移动到正确的路径中.
--allow-unrelated-histories参数仅在git> = 2.9时存在.

  • 对于在那里的OS X人员,请安装`gnu-sed`以使`git-add-repo`函数正常工作。再次感谢安德烈! (2认同)

x-y*_*uri 7

让我使用名称a(代替XXXZZZ)和b(代替YYY),因为这使描述更容易阅读。

假设您想将存储库合并ab(我假设它们并排放置):

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
Run Code Online (Sandbox Code Playgroud)

为此,您需要git-filter-repo安装(filter-branch劝阻)。

合并 2 个大型存储库,将其中一个放入子目录的示例:https : //gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

更多关于它在这里

  • 出色的。历史记录显示在“git log”中没有问题,这与“git subtree add -P ...”的解决方案不同。 (2认同)