我在一个名为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)

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


当前回答

我认为你可以使用'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)

其他回答

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

#!/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

简单的方法是使用git format-patch。

假设我们有两个git存储库foo和bar。

foo包含:

福.txt 。去

栏包含:

酒吧.txt 。去

我们希望以foo结束,其中包含bar历史记录和这些文件:

foo . txt。 git。 雷丁酒吧。. 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]”。

请参阅本文中的基本示例,并考虑在存储库上进行这样的映射:

A <-> yyy, B <-> XXX

完成本章描述的所有活动(合并后),移除分支B-master:

$ git branch -d B-master

然后,推动更改。

这对我很管用。

如果您希望保留第二个存储库的确切提交历史,并因此保留将来轻松合并上游更改的能力,那么下面是您想要的方法。它会导致子树的未修改历史被导入到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 pull -s subtree XXX_remote master

在进行合并之前,Git会自己计算出根的位置,因此您不需要在后续的合并中指定前缀。

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

git log --follow -- a

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

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

2.9之前的Git版本:你不需要给Git merge传递——allow-unrelated-histories选项。

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

原始来源来自github的“子树合并”帮助文章。这是另一个有用的链接。

Git仓库本身就有一个著名的实例,在Git社区中被统称为“有史以来最酷的合并”(以Linus Torvalds在给Git邮件列表中描述这次合并的电子邮件的主题行命名)。在这种情况下,gitk Git GUI现在是Git的一部分,实际上曾经是一个单独的项目。Linus设法将该存储库合并到Git存储库中

它出现在Git存储库中,就好像它一直是作为Git的一部分开发的一样, 所有的历史都保存完好 它仍然可以在旧的存储库中独立开发,只需通过git提取更改即可。

电子邮件包含了复制所需的步骤,但不适合胆小的人:首先,Linus写了Git,所以他可能比你我知道得多一些;其次,这是近5年前的事情了,Git从那时起已经有了很大的改进,所以现在可能要容易得多。

特别是,我猜现在人们会在这种特定情况下使用gitk子模块。