8

I am trying to use git replace to rewrite history and replace the tree of a commit with the tree object of a different commit. I want to make this permanent.

The documentation for git replace seems to indicate this is possible. I adapted the following recipe for replacing a commit from How to prepend the past to a git repository?.

# setup a second branch with a different tree
# existing repo which already contains a few commits
git checkout master -b master2
echo "test file" > testfile.txt      # a different tree
git add testfile.txt
git commit -m "test"

# save the SHA1 of the original tree for later reference
ORIG=$(git rev-parse master^{tree})

# replace the tree of master with the one from master2
git replace master^{tree} master2^{tree}

# verify the contents of the replaced tree
git ls-tree master

this works, master's tree has been replaced by the one from master2, including the extra file:

100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad readme.txt
100644 blob 16b14f5da9e2fcd6f3f38cc9e584cef2f3c90ebe testfile.txt

However, trying to make this permanent, fails:

git filter-branch --tag-name-filter cat -- --all

yields following reply from git:

Rewrite e7619af3e5d424a144845c164b284875c4c20c7a (2/2)
WARNING: Ref 'refs/heads/master' is unchanged
WARNING: Ref 'refs/heads/master2' is unchanged
error: Object bc705106db90164b2e688b4b190d4358f8b09a56 is a tree, not a commit
error: Object bc705106db90164b2e688b4b190d4358f8b09a56 is a tree, not a commit
fatal: ambiguous argument 'refs/replace/e7a75e980c7adc0d5ab1c49ecff082b518bb6cf8^0':
  unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
WARNING: Ref 'refs/replace/e7a75e980c7adc0d5ab1c49ecff082b518bb6cf8' is unchanged

and after I remove the replace reference, master gets its original tree back:

git replace -d ${ORIG}
git ls-tree master

3b18e512dba79e4c8300dd08aeb37f8e728b8dad readme.txt

Why does git filter-branch complain about the replacement and how do I do make the replacement of the tree permanent?

P.S. I know I can do this with a filter-branch --tree-filter or grafts, but (how) can I do it with git replace?

Community
  • 1
  • 1
fraco
  • 101
  • 1
  • 5
  • why would u like to do it with git replace, when there are other ways? – CodeFanatic Mar 06 '14 at 14:35
  • 1
    I think I can do it with tree-filter, but this would be a lot slower and a lot more clunky. I can do it with grafts, but http://stackoverflow.com/questions/6800692/how-do-git-grafts-and-replace-differ-are-grafts-now-deprecated and other links suggest grafts should now be done with git replace. Ultimataly, I don't understand why git is not happy with my incantations, and I would like to understand why. – fraco Mar 06 '14 at 15:41
  • 1
    I have encountered the same problem just today. This link has some relevant information. http://git.661346.n2.nabble.com/BUG-git-filter-branch-does-not-make-tree-replacements-permanent-td7582467.html – ChrisH Mar 08 '14 at 00:30
  • 1
    Are you OK with rewriting the whole history? – the.malkolm Apr 08 '14 at 17:25
  • Beware of unexpected behaviour of the `git replace sha1^{tree} sha2^{tree}`. It does replace reference to already existing tree instead of replacing the tree itself. In other words you must create the tree object before replace another tree with reference to a created one. – Andry Jun 08 '23 at 01:20

1 Answers1

0

Using $ git replace is not recommended thing but if its necessary then you can do this using following steps.

Step1: Go to the branch which you want to replace.

$ git checkout <your_branch_name>

Step2: Run the following command and get the SHA-1 of the replacement commit and replace commit. Replace commit SHA-1 you can get at the top one. And replacement commit SHA-1 is of your choice where you want to replacement of the current branch.

$ git log --status <your_branch_name>

You will get the result like:

$ git log --status master
commit d93378d4e774990160818038eb382f26b29f3c8f master                                                                                                
Author: rajesh <your@mail.com>                                                                                               
Date:   Tue Jul 22 19:00:15 2014 +0000                                                                                                                

    latest commit                                                                                                                                  

commit 08eb045297566d38eec5f8c2c2f987ebd7fbc2b9 master                                                                                                
Author: rajesh <your@mail.com>                                                                                               
Date:   Wed Jul 16 06:44:17 2014 +0000                                                                                                                

    updated file1.txt                                                                                                                                 

commit f4372c6c5d78eca13aa3017d72dc27f5bd38a08d master                                                                                                
Author: rajesh <your@mail.com>                                                                                               
Date:   Wed Jul 16 06:40:36 2014 +0000                                                                                                                

    file1.txt                                                                                                                                         

commit 65614018eb46c797a49cedee97653bb7716b16c2 master                                                                                                
Author: rajesh <your@mail.com>                                                                                                    
Date:   Wed Jul 16 12:03:55 2014 +0530                                                                                                                

    Initial commit

Step3: Now run the following command to replace the current branch to replacement commit.

$ git replace -f d93378d4e774990160818038eb382f26b29f3c8f f4372c6c5d78eca13aa3017d72dc27f5bd38a08d

Step4: Now commit the things using following command.

$ git commit -am 'getting replaced with commit of "file1.txt"'

Step5: Now you can check that you are on the replacement commit.