5

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?

BanksySan
  • 27,362
  • 33
  • 117
  • 216
  • possible duplicate of [how to change remove+add to move in git history](http://stackoverflow.com/questions/1849251/how-to-change-removeadd-to-move-in-git-history) – timrau Sep 04 '13 at 11:48
  • possible duplicate of [Is it possible to move/rename files in git and maintain their history?](http://stackoverflow.com/questions/2314652/is-it-possible-to-move-rename-files-in-git-and-maintain-their-history) – Edward Thomson Sep 04 '13 at 14:19

3 Answers3

4

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

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
2

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).

jthill
  • 55,082
  • 5
  • 77
  • 137
0

No, there is not. Git does not have any notion of file identity. It stores content of directory trees. Only the tree as a whole has notion of history.

There is only one reason to track that a file was renamed instead of old one deleted and new one added and that is to allow merging changes in that file. Since you changed the file (beyond what the rename detection considers similar), there is no chance to merge the changes without conflict anyway, so there should be no practical difference.

Note that Git does handle the merge reason by searching for similar file. It does this in git status and in all merges automatically and in git log, git diff and other commands showing changes if you ask for it. If it does not find the rename, it is unlikely to be possible to do a merge.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • There is another reason, to maintain the file's history. New files don't have any history. – BanksySan Sep 04 '13 at 12:24
  • @BanksySan: A reason would be if you said what is that history good for. Alas you don't. And yes, new files don't have history. **Neither do any other files**. Only project as a whole has history. – Jan Hudec Sep 04 '13 at 12:51
  • @JanHudec: actually similarity-based rename detection was added to Git AFAIK to make it possible to **automatically merge renamed files** - when one side changed file and other side renamed it, Git knows that it has to apply change to file under new name. – Jakub Narębski Sep 04 '13 at 17:45
  • @JanHudec: Note that Git can automatically merge without [textual] conflicts even changed files, if changes touch different parts of file. – Jakub Narębski Sep 04 '13 at 17:47
  • @JanHudec I was thinking of `git log --follow ` – BanksySan Sep 05 '13 at 13:01
  • @BanksySan: `git log --follow` uses the same rename detection algorithm as merge. It's not that useful. Kind of bad habit from older version control systems that didn't have better tools. When you are looking for something particular, `git log -s` (so called "pickaxe") or `git blame` tend to be more useful. – Jan Hudec Sep 06 '13 at 06:03
  • @JanHudec That clears some things up, thanks. I guess that means that `git mv` is actually pretty redundant. It doesn't change anything on the central repo. – BanksySan Sep 09 '13 at 14:11
  • 1
    @BanksySan: `git mv` _old_ _new_ is just shorthand for `mv` _old_ _new_ `;` `git rm` _old_ `;` `git add` _new_. So you can type 1 command instead of 3. – Jan Hudec Sep 09 '13 at 14:14
  • @JanHudec That what I'd concluded. Thanks for the help, appreciated. – BanksySan Sep 09 '13 at 14:33