7

I am in a situation where I am trying to convert an open source project to Git, and I have recently gained access to historical data for the project. I have already made modifications to the repo, though, so I want to add these early changes as git commits to the beginning of the tree of commits in Git. (Yes, I am aware that this will change the SHAs for later commits; this is acceptable.) The data is provided as successive snapshots of the working directory. I want to set it up so that the state of the working directory for the later commits is not affected (I don't want to merge the changes in).

For example, if initial commit B adds files a and b to the working directory, and my historical commit A adds file a, I want to make a new commit B' parented from A that adds file b only. In both B and B', the working directory looks the same, and any subsequent commits on top of B can be safely rebased onto B'.

Is it possible to do this in Git? If so, how?

Edit: Note that I need to modify the initial commit. The standard usage of git commit appends a new commit as a child of the HEAD commit, and so does not work for the initial commit, which has no parent.

3

3 Answers 3

10

Something like this should work.

# Create a new branch with the old history
$ git checkout --orphan old-history    
$ git add <old-files>
$ git commit

# Rebase master on top of the branch with old-history
$ git checkout master
$ git pull --rebase . old-history
3

There are instructions in the git-filter-branch(1) man page for this:

   To set a commit (which typically is at the tip of another history) to be
   the parent of the current initial commit, in order to paste the other
   history behind the current history:

       git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD

   (if the parent string is empty - which happens when we are dealing with
   the initial commit - add graftcommit as a parent). Note that this assumes
   history with a single root (that is, no merge without common ancestors
   happened).

Taking a suggestion from Kent, you might want to add --tag-name-filter cat to this command to rewrite tag names to the new commits. This is also documented in the man page, with a warning to back up the old tags first:

       The original tags are not deleted, but can be overwritten; use
       "--tag-name-filter cat" to simply update the tags. In this case, be
       very careful and make sure you have the old tags backed up in case
       the conversion has run afoul.

The man page also explains why signatures must be stripped when rewriting tags:

       Nearly proper rewriting of tag objects is supported. If the tag has
       a message attached, a new tag object will be created with the same
       message, author, and timestamp. If the tag has a signature
       attached, the signature will be stripped. It is by definition
       impossible to preserve signatures. The reason this is "nearly"
       proper, is because ideally if the tag did not change (points to the
       same object, has the same name, etc.) it should retain any
       signature. That is not the case, signatures will always be removed,
       buyer beware. There is also no support for changing the author or
       timestamp (or the tag message for that matter). Tags which point to
       other tags will be rewritten to point to the underlying commit.

After the single-root example, the man page continues with multiple-root instructions:

   If this is not the case, use:

       git filter-branch --parent-filter \
               'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD

   or even simpler:

       git replace --graft $commit-id $graft-id
       git filter-branch $graft-id..HEAD

Using git filter-branch is much cleaner than using git --rebase, which functions like a combination of diff and patch, applying the changes in each commit on top of another one. Instead, git filter-branch leaves the actual files in each commit untouched and changes the parent commit pointer directly.

Of course, the new commits will have different SHAs than the original ones – that is unavoidable since the parent commit is included in the SHA calculation.


You could also try Mark Lodato's git-reparent script, which appears to use git plumbing commands to achieve a similar result.

0

Im not sure if its possible to just do:

git add [files]
git commit -m 'first commit'

Then merge changes

git add [files]
git commit -m 'second commit'

Then

git push

If this doesnt work then i think there is no way of achieving that.

1
  • 2
    Of course you can do this, but it will add the old commits to the end of the tree, not the start. Aug 3, 2013 at 3:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .