如何将历史记录添加到Git存储库?

she*_*non 18 svn git git-svn

我有一个项目存在于两个SVN存储库中.第二个SVN存储库是通过从旧SVN存储库的结帐添加存储库而不剥离SCM信息来创建的.文件的内容是字节相同的,但没有关联的SCM元数据.

我已经使用了新的SVN存储库并通过git-svn将其移植到Git存储库中.现在我想导入旧的存储库,并以某种方式将其链接到新存储库,以便我可以看到两者的历史记录.有没有一种简单的方法可以将两个存储库手工缝合在一起?

Jak*_*ski 28

另请参阅:如何重新发挥本地Git仓库的我的提交,上一个项目,我在github.com分叉的顶部?问题(我的答案在那里),虽然情况略有不同,但我认为.


你至少有三种可能性:

  • 使用移植物连接两个历史记录,但不要重写历史记录.这意味着您(以及具有相同移植物的任何人)将具有完整历史记录,而其他用户将具有较小的存储库.如果某人已经开始使用较短的历史记录在已转换的存储库上工作,这也可以避免重写历史记录的问题.

  • 使用grafts连接两个历史记录,并使用"git log"或"gitk"(或其他Git历史记录浏览器/查看器)检查它是否正确,然后使用git filter-branch 重写历史记录 ; 然后你可以删除移植文件.这意味着从重写的存储库中克隆(提取)的每个人都将获得完整的连接历史记录.但是,如果某人已经基于转换的短历史存储库(但这种情况可能不适用于您),则重写历史记录是一个很大的问题.

  • 使用git replace连接两个历史记录.这将允许人们通过选择获取refs/replace/(然后他们获得完整的历史记录)来选择他们是想要完整历史记录还是仅仅是当前历史记录(然后他们获得简短的历史记录).不幸的是,这需要使用尚未发布的Git版本,使用开发('master')版本,或1.6.5的候选版本之一.该refs/replace/层次计划为即将到来的Git版本1.6.5.


下面是所有这些方法的分步说明:移植(本地),使用移植重写历史,以及refs/replace /.

在所有情况下,我都假设您在单个存储库中同时拥有当前和历史存储库历史记录(您可以使用git remote add从另一个存储库添加历史记录).我还假设短历史存储库中的(其中一个)分支名为"master",并且要附加当前历史记录的历史存储库的分支(commit)称为"历史记录".您必须替换自己的分支名称(或提交ID).

找到提交附件(短历史的根)

首先,您必须在短历史中找到要附加到完整历史记录的(SHA-1标识符)提交.它将是短历史中的第一个提交,即根提交(没有任何父母的提交).

有两种方法可以找到它.如果您确定没有任何其他root提交,则可以使用以下命令找到拓扑顺序中的最后一个(最下面的)提交:

$ git rev-list --topo-order master | tail -n 1
Run Code Online (Sandbox Code Playgroud)

(tail -n 1用于获取输出的最后一行;如果没有,则不需要使用它.)

如果有多个root提交的可能性,您可以使用以下单行查找所有无父提交:

$ git rev-list --parents master | grep -v ' '
Run Code Online (Sandbox Code Playgroud)

(其中grep -v ' ',单引号之间的空格用于过滤掉所有具有父项的提交).然后,git show <commit>如果有多个提交,则必须检查(使用例如" ")这些提交,并选择一个要附加到早期历史记录的提交.

我们称之为提交TAIL.您可以使用(假设更简单的方法适合您)将其保存在shell变量中:

$ TAIL=$(git rev-list --topo-order master | tail -n 1)
Run Code Online (Sandbox Code Playgroud)

在下面的描述中,我将使用$TAIL表示您必须在当前(短)历史记录中替换最底层提交的SHA-1 ...或者允许shell为您进行替换.

查找附加到(历史存储库顶部)的提交

这部分很简单.我们必须将提交的符号名称转换为SHA-1标识符.我们可以使用"git rev-parse"来做到这一点:

$ git rev-parse --verify history^0
Run Code Online (Sandbox Code Playgroud)

(如果'history'是标记,则使用'history ^ 0'代替'history';我们需要提交的SHA-1,而不是标记对象的SHA-1).类似地,就像找到要附加的提交一样,让我们​​将此提交ID命名为TOP.您可以使用以下命令将其保存在shell变量中:

$ TOP=$(git rev-parse --verify history^0)
Run Code Online (Sandbox Code Playgroud)

使用移植文件加入历史记录

位于.git/info/grafts(如果不存在,则需要创建此文件,如果要使用此机制)的grafts文件用于替换提交的父信息.它是基于行的格式,其中每行包含我们想要修改的提交的SHA-1,后面是零或更多空格分隔的提交列表,我们希望给定提交作为父项; 与" git rev-list --parents <revision>"输出相同的格式.

我们想要$ TAIL提交,其中没有任何父母,将$ TOP作为其单亲.所以在info/grafts文件中应该有一行与$ TAIL提交的SHA-1,由空间用$ TOP提交的SHA-1分隔.您可以使用以下单行程序(请参阅git filter-branch文档中的示例):

$ echo "$TAIL $TOP" >> .git/info/grafts
Run Code Online (Sandbox Code Playgroud)

现在你应该检查,使用"git log","git log --graph","gitk"或你正确加入历史记录的其他历史浏览器.

根据移植文件重写历史记录

请注意,这将改变历史!

要使grafts文件中记录的历史永久化,使用"git filter-branch"来重写所需的分支就足够了.如果只有一个分支需要重写('master'),它可以很简单:

$ git filter-branch $TOP..master
Run Code Online (Sandbox Code Playgroud)

(这将只处理最小的提交集).如果有更多分支受到加入历史记录的影响,您可以简单地使用

$ git filter-branch --all
Run Code Online (Sandbox Code Playgroud)

现在您可以删除移植文件.检查是否所有内容都符合您的要求,并删除备份refs/original/(有关详细信息,请参阅"git filter-branch"文档).

使用refs/replace/mechanism

这是移植文件的替代方案.它具有可转移的优点,所以如果你发布了短暂的历史并且不能重写它(因为其他基于短期历史的工作),那么使用refs/replace /可能是一个很好的解决方案......好吧,至少当Git版本1.6.5发布时.

refs/replace/mechanism的操作与grafts文件的操作不同:您可以替换对象,而不是修改父级的信息.首先,您必须创建一个与$ TAIL具有相同属性的提交对象,但将$ TOP作为父级.

我们可以用

$ git cat-file commit $TAIL > TAIL_COMMIT
Run Code Online (Sandbox Code Playgroud)

(临时文件的名称只是一个例子).

现在你需要编辑'TAIL_COMMIT'文件(它看起来像这样):

tree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038
author Joe R Hacker  1112911993 -0700
committer Joe R Hacker  1112911993 -0700

Initial revision of "project", after moving to new repository

现在你需要将$ TOP添加为父级,方法是在'tree'标题和'author'标题之间添加一行"parent $ TOP"(其中$ TOP必须扩展为SHA-1 id!).编辑'TAIL_COMMIT'后,它应该如下所示:

tree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038
parent 0f6592e3c2f2fe01f7b717618e570ad8dff0bbb1
author Joe R Hacker  1112911993 -0700
committer Joe R Hacker  1112911993 -0700

Initial revision of "project", after moving to new repository

如果需要,可以编辑提交消息.

现在,您需要使用git hash-object在存储库中创建新的提交.您需要保存此命令的结果,该命令是新提交对象的SHA-1,例如:

$ NEW_TAIL=$(git hash-object -t commit -w TAIL_COMMIT)
Run Code Online (Sandbox Code Playgroud)

(这里' -w'选项实际上是将对象写入存储库).

最后使用git replace将$ TAIL替换为$ NEW_TAIL:

$ git replace $TAIL $NEW_TAIL
Run Code Online (Sandbox Code Playgroud)

现在,如果历史记录正确,还需要检查(使用"git log"或其他一些历史记录查看器).

现在任何想要拥有完整历史记录的人都需要添加' +refs/replace/*:refs/replace/*'作为pull refspecs之一.

最后说明: 我没有检查过这个解决方案,因此您的里程可能会有所不同.


bdo*_*lan 5

首先,创建一个移植点来附加两个历史.然后在存储库上运行git filter-branch以使更改成为永久更改.注意,这将改变移植物下游所有提交的提交ID.