如何将 git 存储库合并到维护提交历史记录的父存储库中?

How to merge a git repo into a parent repo maintaining commit history?

我一直在跟踪 child 存储库中的文件。但现在我还需要跟踪另一个 child,所以我将它们重命名为 child1child2。我只想维护一个包含这两个文件夹的 parent 目录,但我不想丢失 child 存储库中的提交。

之前:

/
/docs/
/docs/child/
/docs/child/.git/           # repo at child level
/docs/child/file-a
/docs/child/file-b

之后:

/
/docs/
/docs/parent/
/docs/parent/.git/          # parent repo contains all files and child1 commits
/docs/parent/child1/
/docs/parent/child1/file-a
/docs/parent/child1/file-b
/docs/parent/child2/
/docs/parent/child2/file-c

如何实现这个简单的设置?

请记住,Git 存储库包含 提交 ,而不是文件。 (然后提交包含文件,但我们是逐个提交,而不是逐个文件。)

您存储在 docs/child 中的现有存储库(作为其中的 .git 目录)包含一系列提交。每个提交都有每个文件的完整快照。1 例如,这些提交中的文件是 file-afile-b

您现在希望同一个存储库添加 new 提交,其中提交中的文件被命名为 child1/file-achild1/file-b 和 [=16] =] 例如。这很容易做到:进入存储库工作树,创建 child1child2 子目录,以及——为了方便2——使用git mvfile-a重命名为child1/file-a,将file-b重命名为child1/file-b。创建新文件 child2/file-c,在其上使用 git add,然后 运行 git commit,然后向现有存储库添加一个新提交;在这个新提交中,内容存储在这些新名称下,而在所有现有提交中,快照将文件存储在它们的旧名称下。

请注意 Git 不存储目录:它仅存储 文件,其名称可能包含也可能不包含嵌入(正向)斜杠。 Git 将根据需要 创建 您的 OS 需要的目录,因为您的 OS 坚持认为没有文件这样的东西名为 child1/file-a:这是一个名为 child1 的目录,其中包含一个名为 file-a 的文件。 Git 坚持认为,不,这是一个名为 child1/file-a 的文件; Git 完全解决了3 Git 的文件理念与您的 OS 的不匹配问题。

记住:一个 Git 存储库保存提交。 Git 不是关于文件或分支,而是关于提交。提交保存文件(我们需要这些文件来完成我们的工作),分支名称帮助我们(和 Git)找到我们想要的包含我们需要的文件的提交。但是在存储库级别,Git 大约是 提交 。当您考虑存储在 Git 存储库中的内容时,请考虑提交。每个文件都包含所有文件的快照,外加一些元数据。


1commits 中的文件被压缩并 Git 化,因此只有 Git 可以读取它们,几乎没有任何东西可以写入它们.它们也是 去重(在提交内和跨提交)所以每次提交每次都保存每个文件的事实不会导致存储库膨胀到荒谬的大小(尽管一些二进制文件打败这个把戏,然后存储库 确实 膨胀到荒谬的大小,这就是为什么在 Git).

中存储大型二进制文件是不明智的原因

2就 Git 而言,删除一些名为 file-a 的文件与创建一个全新的 child1/file-a 没有区别包含相同内容的文件,而不是将现有 file-a 重命名为 child1/file-a。最后的 commits 只是保存内容;如果新提交中 child1/file-a 的内容与旧提交中 file-a 的内容逐字节匹配,则它们将被删除。尽管如此,在这里使用 git mv 还是要方便得多,除非你已经使用了普通的 mv 或你的 OS 使用的任何重命名或重组命令。如果是这样,请随意使用 git rm and/or git add 来更新 Git 对文件名称和内容的想法。 Git 不会关心你是如何从旧设置转到新设置的:它只包含 提交 ,而这些提交只包含文件的快照(加上元数据,但是元数据不包含重命名信息)。

3对于“完全”的某些值:directory/file Git 中的冲突代码有偶尔出现小错误的历史。总而言之,考虑到 Git 和 OS 之间的这种不匹配有多么复杂,这非常好。尽管如此,在某些导致文件移动到新目录的提交中复杂的事后检测重命名的情况下,Git 的某些版本比其他版本更好。

根据 torek 的回答,因为我不需要将两个子回购合并到一个父回购中,所以我最终重新使用初始 child 回购作为父回购。我所做的是:

# enter the child repo
cd child
# create the new child folder
mkdir child1
# move all files inside "child" from their original location to their multi-child destination
# the -k flag avoids the error caused by copying child1 into child1
# this operation doesn't copy files or folders starting with a dot
git mv -k * ./child1
# running `git add -A && git status` would show that all files have been renamed (moved)
# create child2 and move files there
mkdir child2

现在结构如下所示:

/
/docs/
/docs/child/
/docs/child/.git/
/docs/child/child1/
/docs/child/child1/file-a
/docs/child/child1/file-b
/docs/child/child2/
/docs/child/child2/file-c

现在,如果需要,可以将“子”文件夹重命名为“父”文件夹,并在其中 运行 git config -e 指向另一个远程仓库。