我如何在git repo上添加历史记录? [英] How do I prepend history to a git repo?

查看:94
本文介绍了我如何在git repo上添加历史记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个存在于两个SVN仓库中的项目。第二个SVN回购仅仅是通过在没有SCM信息剥离的情况下从旧SVN仓库的结账中添加回购来创建的。这些文件的内容是字节相同的,但没有关联的SCM元数据。



我将新SVN回购并通过git移植到Git回购-svn。现在我想导入旧的回购协议,并以某种方式将其链接到新的回购协议,以便我可以在两者之间查看历史记录。有没有一种简单的方法来做到这一点,而无需手动拼接两个回购站?

如何在我在github.com上分支的项目之上重新播放本地git repo的提交?问题 (和

hr>

您至少有三种可能性:使用 a href =http://git.or.cz/gitwiki/GraftPoint =nofollow noreferrer>移植 加入两个历史记录,但不要重写历史记录。这意味着你(和任何具有相同移植物的人)将具有完整的历史记录,而其他用户将具有较小的存储库。这也可以避免重写历史时出现的问题,如果有人已经开始在历史较短的转换后的版本库上工作的话。 使用移植来加入两个历史记录,检查它是正确使用git log或gitk(或其他git历史浏览器/查看器),然后重写历史记录,使用 git filter-branch ;那么你可以删除移植文件。这意味着每个从重写仓库克隆(提取)的人都会获得完整的历史记录。但是如果有人已经基于转换后的短期存储库(但这种情况可能不适用于您),那么重写历史记录是一个很大的问题。 使用 git替换 加入两个历史。这将允许人们通过选择获取 refs / replace / (然后他们获得完整的历史记录)或者不选择它们来选择他们是想要完整的历史记录还是仅仅是当前的历史记录获得较短的历史)。不幸的是,这需要目前使用尚未发布的版本的git,使用开发('主')版本,或1.6.5版本的候选版本之一。

$计划了 refs / b $ b




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



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



寻找提交附件(短历史记录根)



首先,您必须在短历史中查找(SHA-1标识符)提交,以便将其附加到完整历史记录中。这将是短历史上的第一次提交,即根提交(没有任何父母的提交)。

有两种方法可以找到它。如果您确定没有任何其他根提交,可以使用以下拓扑顺序查找最后(最底部)提交:

  $ git rev-list --topo-order master | tail -n 1 

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



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

  $ git rev-list --parents master | grep -v''

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



我们称之为TAIL。您可以使用(假设简单的方法适用于您)将它保存在shell变量中:

  $ TAIL = $(git rev- list --topo-order master | tail -n 1)

在下面的描述中, code> $ TAIL 意味着你必须替换当前(短)历史记录中最底层提交的SHA-1 ...或允许shell为你替换。



查找提交附加到(历史资源库顶部)



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

  $ git rev-parse --verify history ^ 0 

(其中'history ^ 0'代替'history',以防万一'history'是标签;我们需要提交SHA-1,而不是标签对象)。类似于查找提交附加,让我们命名这个提交ID TOP。您可以使用

  $ TOP = $(git rev-parse --verify history ^ 0)$ b将它保存在shell变量中$ b  



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



移植文件,位于 .git / info / grafts (如果你想使用这个机制,你需要创建这个文件,如果你不想使用这个机制)提交。它是基于行的格式,其中每行包含我们想要修改的提交的SHA-1,后面跟着零个或多个空格分隔的提交列表,我们希望给定提交作为父列;与 git rev-list --parents< revision> 输出相同的格式。



TAIL承诺,没有任何父母,以$ TOP作为其单亲。因此,在 info / grafts 文件中,应该有与$ TAIL commit的SHA-1一致的行,由$ TOP commit的SHA-1以空格分隔。您可以使用下面的一行代码(另请参阅 git filter-branch

  $ echo$ TAIL $ TOP>> .git / info / grafts 

现在您应该使用git log,git log - -graph,gitk或其他历史浏览器,您可以正确加入历史记录。

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



请注意,这会改变历史记录!



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

  $ git filter-branch $ TOP..master 

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

  $ git filter-branch --all 

现在您可以删除移植文件。检查一切是否如你所愿,并删除 refs / original / 中的备份(详情请参阅git filter-branch的文档)。



使用refs / replace / mechanism



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



refs / replace /机制的运行方式与移植文件不同:不是修改父项信息,而是替换对象。因此,首先您必须创建与$ TAIL具有相同属性的提交对象,但是$ TOP作为父项。



我们可以使用

  $ git cat-file commit $ TAIL> TAIL_COMMIT 

(临时文件的名称只是一个例子)。现在你需要编辑TAIL_COMMIT文件(看起来像这样):

 
tree 2b5bfdf7798569e0b59b16eb9602d5fa572d6038
作者Joe R Hacker 1112911993 -0700
提交者Joe R Hacker 1112911993 -0700

移至新存储库后project的初始修订

现在,您需要在tree标题和author标题之间添加$ TOP作为父项,将parent $ TOP(其中$ TOP必须扩展为SHA-1 id!编辑'TAIL_COMMIT'应该看起来像这样:

 
树2b5bfdf7798569e0b59b16eb9602d5fa572d6038
父母0f6592e3c2f2fe01f7b717618e570ad8dff0bbb1
作者Joe R Hacker 1112911993 - 0700
提交者Joe R Hacker 1112911993 -0700

移至新存储库后项目的初始修订

如果你愿意,你可以编辑提交信息。



现在你需要使用 git hash-object 在存储库中创建新的提交。你需要保存这个命令的结果,这个命令是一个新的提交对象的SHA-1,例如这样:

  $ NEW_TAIL = $(git hash-object -t commit -w TAIL_COMMIT)

(其中'<$ c

最后使用 -w '选项在这里实际将对象写入存储库) www.kernel.org/pub/software/scm/git/docs/git-replace.htmlrel =nofollow noreferrer> git replace 以$ NEW_TAIL取代$ TAIL:

  $ git替换$ TAIL $ NEW_TAIL 





现在,任何想要完整记录的人都可以查看(使用git log或其他历史记录查看器)需要添加' + refs / replace / *:refs / replace / * '作为pull refspecs之一。



最后说明 我没有检查过这个解决方案,所以YMMV


I have a project that has existed in two SVN repositories. The second SVN repo was created simply by adding the repos from a checkout of the old SVN repository without SCM info stripped. The content of the files are byte identical, but there is no associated SCM meta-data.

I have taken the new SVN repo and ported it into a Git repo via git-svn. Now I would like to import the old repo and somehow get it to link the new repo so I can see the history across both. Is there a simple way to do this without hand stitching the two repos together?

解决方案

See also: How do I re-play my commits of a local git repo, on top of a project I forked on github.com? question (and my answer there), although the situation is slightly different, I think.


You have at least three possibilities:

  • Use grafts to join two histories, but do not rewrite history. This means that you (and anybody who has the same grafts) would have full history, while other users would have smaller repository. This also avoids problems with rewritten history if somebody already started working on top of converted repository with shorter history.

  • Use grafts to join two histories, check that it is correct using "git log" or "gitk" (or other git history browser/viewer), then rewrite history using git filter-branch; then you can remove grafts file. This means that everybody who clones (fetches) from rewritten repository would get full, joined history. But rewriting history is a big no if somebody already based work on converted short-history repository (but this case might not apply to you).

  • Use git replace to join two histories. This would allow people to select whether they want full history, or just current history, by choosing to fetch refs/replace/ (then they get full history) or not (then they get short history). Unfortunately this requires currently to use yet unreleased version of git, using development ('master') version, or one of release candidates for 1.6.5. The refs/replace/ hierarchy is planned for upcoming git version 1.6.5.


Below there are step by step instructions for all those methods: grafts (local), rewriting history using grafts, refs/replace/.

In all cases I assume that you have both current and historical repository history in single repository (you can add history from another repository using git remote add). I also assume that (one of) branch in short-history repository is named 'master', and that branch (commit) of the historical repository where you want to attach current history is called 'history'. You would have to substitute your own branch names (or commit IDs).

Finding commit to attach (root of short history)

First you have to find (SHA-1 identifier of) commit in short-history that you want to attach to full history. It would be the first commit in short history, i.e. the root commit (the commit without any parents).

There are two ways of finding it. If you are sure that you do not have any other root commit, you can find last (bottommost) commit in topological order, using:

$ git rev-list --topo-order master | tail -n 1

(where tail -n 1 is used to get last line of output; you don't need to use it if you don't have it).

If there is possibility of multiple root commits, you can find all parentless commits using the following one-liner:

$ git rev-list --parents master | grep -v ' '

(where grep -v ' ', that is space between single quotes, is used to filter out all commits which have any parents). Then you have to check (using e.g. "git show <commit>") those commits if there are more than one, and select one that you want to attach to earlier history.

Let's call this commit TAIL. You can save it in shell variable using (assuming that simpler method works for you):

$ TAIL=$(git rev-list --topo-order master | tail -n 1)

In the description below I would use $TAIL to mean that you have to substitute SHA-1 of bottommost commit in current (short) history... or allow shell to do the substitution for you.

Finding commit to attach to (top of historical repository)

This part is simple, we have to convert symbolical name of commit into SHA-1 identifier. We can do this using "git rev-parse":

$ git rev-parse --verify history^0

(where 'history^0' is used in place of 'history' just in case if 'history' is a tag; we need SHA-1 of commit, not of a tag object). Similarly like finding commit to attach, let's name this commit ID TOP. You can save it in shell variable using

$ TOP=$(git rev-parse --verify history^0)

Joining history using grafts file

The grafts file, located in .git/info/grafts (you need to create this file if it doesn't exist, if you want to use this mechanism) is used to replace parent info for a commit. It is line based format, where each line contains SHA-1 of a commit we want to modify, followed by zero or more space-separated list of commits we want for given commit to have as parents; the same format that "git rev-list --parents <revision>" outputs.

We want $TAIL commit, which doesn't have any parents, to have $TOP as its single parent. So in info/grafts file there should be line with SHA-1 of $TAIL commit, separated by space by SHA-1 of $TOP commit. You can use the following one-liner for this (see also examples in git filter-branch documentation):

$ echo "$TAIL $TOP" >> .git/info/grafts

Now you should check, using "git log", "git log --graph", "gitk" or other history browser that you joined histories correctly.

Rewriting history according to grafts file

Please note that this would change history!

To make history as recorded in grafts file permanent, it is enough to use "git filter-branch" to rewrite branches you need. If there is only single branch that needs to be rewritten ('master'), it can be as simple as:

$ git filter-branch $TOP..master

(this would process only minimal set of commits). If there are more branches affected by joining history, you can use simply

$ git filter-branch --all

Now you can delete grafts file. Check if everything is like you wanted, and remove backup in refs/original/ (see documentation for "git filter-branch" for details).

Using refs/replace/ mechanism

This is an alternative to grafts file. It has the advantage that it is transferable, so if you published short history, and cannot rewrite it (because other based their work on short history), then using refs/replace/ might be a good solution... well, at least when git version 1.6.5 gets released.

The refs/replace/ mechanism operates differently than grafts file: instead of modifying parents information, you replace objects. So first you have to create commit object which has the same properties as $TAIL, but has $TOP as a parent.

We can use

$ git cat-file commit $TAIL > TAIL_COMMIT

(the name of temporary file is only an example). Now you need to edit 'TAIL_COMMIT' file (it would look like this):

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

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

Now you need to add $TOP as parent, by putting line with "parent $TOP" (where $TOP has to be expanded to SHA-1 id!) between 'tree' header and 'author' header. After editing 'TAIL_COMMIT' should look like this:

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

If you want, you can edit commit message.

Now you need to use git hash-object to create new commit in the repository. You need to save result of this command, which is SHA-1 of a new commit object, for example like this:

$ NEW_TAIL=$(git hash-object -t commit -w TAIL_COMMIT)

(where '-w' option is here to actually write object to repository).

Finally use git replace to replace $TAIL by $NEW_TAIL:

$ git replace $TAIL $NEW_TAIL

Now what is left to check (using "git log" or some other history viewer) if the history is correct.

Now anybody who wants to have full history needs to add '+refs/replace/*:refs/replace/*' as one of pull refspecs.

Final note: I have not checked this solution, so YMMV

这篇关于我如何在git repo上添加历史记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆