【问题标题】:Git: Copy history of file from one repository to another [duplicate]Git:将文件的历史记录从一个存储库复制到另一个[重复]
【发布时间】:2017-11-30 07:35:27
【问题描述】:

我有两个 git 存储库 A 和 B,它们都包含一个名为 file1.cc 的文件。 是否可以将 repo A 中 file1.cc 的历史合并/复制到 repo B 中的 file1.cc?

问题是我们已经将文件从存储库 A 移动到存储库 B,并且所有文件的历史记录都丢失了。但现在一些开发人员已经开始着手处理 repo B 并推动他们的更改。所以现在我想要从 repo A 到 repo B 的某些文件的合并/复制历史记录,这些文件仅适用于某些文件。有可能这样做吗?还是曾经丢失的文件的历史就永远丢失了?

请帮忙。提前致谢。

【问题讨论】:

  • 你能详细说明你为什么要这样做吗?

标签: git


【解决方案1】:

可以做到,但可能并不容易。但首先要做的是:没有“移动文件的历史”。只有移动提交,所以如果您想要代表文件子集历史的提交,那么创建这些提交是第一个挑战。

最简单的方法是传输所有历史。 (事实上​​,如果你将 Repo B 制作为 Repo A 的浅表克隆,那么你可以将其取消浅化并完成。但我猜这不是你创建 Repo B 的方式......)

无论如何,既然您要从回购 A 转移到回购 B,也许您特别想删除一些历史记录。这可能是一个完整的主题,但我们假设您真的只想要几个文件的历史记录。

在您想要的所有文件(而不是其他文件)都在子目录中的特殊情况下,并且您希望(或至少可以接受)将这些文件移动到 repo 的根目录,您可以使用 @987654322 @ 与--subdirectory-filter

更一般地,如果我们假设路径不应该改变并且您想要的文件可能位于树中的任何位置,那么您可以使用filter-branch--index-filter

git filter-branch --index-filter 'git rm --cached --ignore-unmatch each file or *glob* you do NOT want' --prune-empty -- all

如果 repo 有很多提交,这可能需要一段时间。如果rm 的文件列表不重要,您可能希望将多个git rm 命令放入一个shell 脚本中,并将其用作--index-filter 参数,而不是如上所示内联它。

好吧,无论如何,希望你有一段历史你想移植到回购 B 中。

cd repo-b
git remote add repo-a path/to/repo-a
git fetch repo-a

现在你在回购 B:

... A -- B <--(repo-a/master)
  \
   (repo-a/other-branches-maybe)

B' -- C -- D (master)(origin/master)

所以我在这里做一个假设,来自回购 A 中最后一次 master 提交的 TREE - 我们的历史重写创建的那个 B - 或至少该树的一部分,是在 Repo B 中作为根提交导入。

现在您有三个选项:重新父代、变基或替换

由于我假设最近的历史状态比旧的历史状态更重要,而且旧的历史只是被添加以供参考,最安全的做法是将C 重新设置为B。 (您可以选择将 B' 重新设置为 A,但我认为这没有太大区别......)

因此,您可以从https://git-scm.com/docs/git-filter-branchfilter-branch 文档中汲取灵感

# be sure you're on master
echo "$commit-id $graft-id" >> .git/info/grafts
git filter-branch $graft-id..HEAD

其中$commit-idB 的SHA,$graft-idC 的SHA

rebase 可能会更简单一些(假设历史之间有一定程度的一致性),但会引入您最终修改 D 的树的可能性。如果您确实决定尝试变基,那将是

git rebase --onto repo-A/master B' master

其中B' 是 Repo B 根提交的 SHA ID。 (交替

git rebase --interactive --onto repo-A/master --root master

然后删除B' 的条目。)

这些选项中的任何一个都将重写提交CD。 (即使重新设置确保TREE 不变,提交仍会被替换。)您的开发人员必须将其视为上游变基(请参阅“从上游变基恢复”下的git rebase 文档)。为了缓解这种情况,我通常建议进行协调切换,开发人员检查他们拥有的所有东西,丢弃他们的克隆,然后你进行重写,他们从新的 repo 重新克隆。

如果你想避免重写,你可以使用第三个选项:git replace。众所周知,这有一些怪癖,并且需要正确设置每个克隆才能“查看”拼接历史。

因此,为了支持这一点,您只需标记 B(也许还有 B'):

git tag old-history repo-a/master
git tag new-root B'

(其中B' 是适当的 SHA 值 ID,或等效表达式)。

当有人克隆 repo 时,他们只会看到新的历史记录,但他们可以说

git replace new-root old-history

这将掩盖历史的突破。

一旦您完成了 reparent、rebase 或 replace - 您可以删除 repo-a 遥控器。

【讨论】:

  • 这确实是我想要实现的目标。非常感谢。
  • 我也尝试过使用 gitpython 编写脚本,但这需要一些时间:(.
  • 这对我来说很有效,但一开始让我很反感的是,--index-filter 示例中的单个刻度在我的浏览器中看起来像反引号 (`)。我试了几次才意识到它们应该是单个刻度 (')。
  • 对不起,那是我的错误;每当我有很多单引号和反引号的帖子时,拼写错误就会蔓延
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-21
相关资源
最近更新 更多