Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restore moved file's history in Git

If a file has had it's name and content changed, is it possible to mark the move after the commit has taken place?

e.g.

  1. file1.txt changed to file2.txt and content changed.
  2. git commit
  3. Git thinks file1.txt has been deleted and file2.txt is an unrelated, new file.

Is there a way to flag this as a rename after the commit has happened?

like image 523
BanksySan Avatar asked Sep 04 '13 11:09

BanksySan


2 Answers

No, it isn't, because Git doesn't do rename tracking... but it does rename detection
(based on how similar is contents and name of files).

This means that

  • git log --follow file2.txt would follow file history through rename (note: this unfortunately not always work correctly, as --follow is a bit of hack),
  • git blame -C -C file2.txt would follow line-wise file history across file renames, content copying and moving across files and inside file
  • git show -C would show that there was file rename (you can configure Git to always do rename and optionally even copy detection when displaying diffs).

HTH

like image 142
Jakub Narębski Avatar answered Sep 30 '22 05:09

Jakub Narębski


git won't detect the combination of a really substantial change and a simultaneous move as a rename, as you've discovered.

The thing to understand is that git detects the changes in a commit each time you look at the history, by comparing the commit's contents with its parent commit. That may seem too simple, but it turns out it's the good kind of simple.

So you need to insert a new commit, a rename that git log etc. can detect as such, followed by the rest of the history you've accumulated since then, and that means a new history for your branch, i.e. a rebase or cherry-pick.

So:

# make a new history with a rename that can be detected by comparing commits
# check out the glitched commit's ancestor by its sha, 
# this work won't take enough time to bother inventing a transient name for it
git checkout commit-before-rename+bigchange      
git mv file1.txt file2.txt
git commit
git checkout commit-with-rename+bigchange -- file2.txt
git commit

# Now add the rest of the changes, also detectable by comparing commits,
# by re-applying them on top of this new history:
git cherry-pick commit-before-rename+bigchange..branch-it\'s-on

# and force the old branch name to refer to this new history you just made
# the old history will be garbage-collected after 90 days or so if
# it's not part of the history of any other references
git checkout -B branch-it\'s-on 

And you're done, except if any other repositories have acquired the messed-up history by fetch or push or clone, then you'll have to force a push or get those repo's owners to force a refetch (and any subsequent history they've built on top of that, which is why you really really don't wan't to publish too soon).

like image 27
jthill Avatar answered Sep 30 '22 06:09

jthill