47

How do each of these VCS handle renames?

I have found a lot of contradicting information stating that git tracks LOC (lines of code) instead of files, renames would therefore have no meaning for it.

Justin M. Keyes
  • 6,679
  • 3
  • 33
  • 60
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172
  • 2
    bzr tries very hard to do the Right Thing WRT renaming: http://www.markshuttleworth.com/archives/123 . How is that in practice? I don't know. I use mostly git and svn. – bendin Oct 08 '09 at 14:17
  • 3
    And Mark S. tried very hard to spread FUD about other VCSs being unsafe wrt. renaming... – tonfa Oct 08 '09 at 14:20
  • 5
    bzr may very well do that one thing right, but I find nearly every other aspect of the system horribly frustrating. The only stuff I work on that uses bzr uses it because of launchpad which is also horribly frustrating and locks you into their revision control system. It's the one thing that canonical does that makes me really not like them. – Dustin Oct 08 '09 at 16:52

6 Answers6

44
  • Git doesn't track renames at all, but uses heuristic to re-discover them during merge etc.
  • Mercurial tracks renames (the origin version and origin file is recorded) and uses that information during merges. So you have to explicitly tell hg about renames with hg mv, or use hg addremove --similarity for auto-discovery. There has been some talk about adding heuristics during merge too.
  • Svn tracks renames, but I don't know how good it deals with them during merges (never actually tested that).
tonfa
  • 24,151
  • 2
  • 35
  • 41
  • 2
    SVN generates a tree conflict when you try to merge to a file that has been renamed, so that the user can decide how to handle it correctly. Older versions would merely output a "skipped" warning saying that the merge target doesn't exist. – Wim Coenen Oct 08 '09 at 13:49
  • 7
    SVN does not track renames. A rename in SVN is recorded as one file being removed and another being added. Rename tracking is planned for SVN 1.8, due out sometime next year, but how effective this will be remains to be seen. – jammycakes Jul 04 '10 at 16:05
  • 4
    rename tracking in SVN was planned for 1.4, 1.5, and 1.6. I suspect attaching rename tracking to version 1.8 is just clever way of saying *not yet*. – deft_code Oct 13 '10 at 19:36
  • 2
    @jammycakes: As far as I've experienced, SVN tracks renames. I've seen it recorded as one file being removed and another being *copied* from the original, not just added as a new file. – Ole Lynge Nov 25 '10 at 18:19
  • 1
    @OleLynge: Yes, that's just the point. SVN currently tracks a rename as "copy & delete". It does not, however, record that the copy and the delete were part of one rename. – sleske Oct 14 '11 at 12:24
  • In my experience with both svn and git, rename tracking invariably & frustratingly breaks (loosing history and requiring hand holding to manually fix up) ... whereas git's rename detection _just works_. – Tony O'Hagan Mar 23 '12 at 04:38
  • @TonyO'Hagan you meant that both svn and mercurial rename breaks right? I've started using mercurial not too long ago and is so frustrating compared to git. – yorch Nov 07 '14 at 15:57
  • @yorch I've not used mercurial - but if it uses the same strategy as svn then it will suffer the same issue. – Tony O'Hagan Nov 08 '14 at 01:33
  • Git uses multiple strategies to detect renames. If the file content has not changed, it will easily auto detect this as all git file objects are stored under a hash of their content. However, git goes one better and also detects add/remove pairs in a single checkin that share a large percentage of similar content and assumes they are a rename + diff. – Tony O'Hagan Nov 08 '14 at 01:39
23

Git

Git is different in that it doesn't do rename tracking, which means that it doesn't need to be told about renames by using SCM commands to do rename (or run autodetection script to mark renames before commit), and doesn't save such information in the repository, but it does rename detection. This means that it finds renames using filename and file contents similarity based heuristic algorithm, both during merge, and for diff when requested via -M option (or configured using diff.renames config option).

The advantages of this method are the following:

  • renames doesn't need to be marked (or detected) explicitely: the renames can come from a patch, or can be done vie filemanager or graphical interface
  • the similarity detection algorithm can be improved, and is not frozen at the time of commit as in the case of detecting renames to mark them before commit, and saving this information in repository; it is also easier to deal with rename detection mistakes if they are not frozen in history
  • it follows Git ophilosophy that it is contents that matters; see how git blame (and graphical frontends to it like "git gui blame") can follow movement of blocks of code across file boundaries, something that is more generic than wholesame renames of files.
  • the same mechanism is responsible for handling renames during merges; using rename detection means that it can be done for 3 commits that merge is ultimately based on, and there is no need to carefully follow history, noting each rename - or a non-distributed concept of canonical name of a file

Note that pathspec filtering do not work well with rename detection; if you want to follow history of a file across renames use "git log --follow <filename>"

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 2
    That 'git log --follow' is a hidden gem. – Gregg Lind Oct 21 '09 at 07:18
  • 1
    Not freezing rename detection at commit time seems like a double-edged sword to me. Yes, improved rename detection heuristics can be developed later and improve the detection of past renamings "for free", but OTOH I could run `git log --follow xyz` today, and tomorrow, without anybody changing `xyz` in between, and get different results. – j_random_hacker Nov 23 '17 at 13:31
10

In practice:

Git detects renames automatically. (Incidentally, I've heard claims that git can detect when you move a function from one file to another. My initial tests seem to indicate that this is not the case, however.)

With Mercurial, you have to explicitly tell it about renames, either by hg mv, or the --similarity option of hg addremove, or TortoiseHg's "guess renames" option, or certain tools e.g. VisualHg will flag the renames for you. If you want to use the Git approach with Mercurial, I've written an extension to detect renames at commit time, but it's at a very experimental stage at the moment.

Subversion doesn't handle renames at all. It records a rename as one file being removed and another file being added. This means, for instance, that if Alice changes a file and Bob renames it, you get a tree conflict. This happens whether you are doing full-blown branching and merging or simply svn update. Rename tracking is planned for Subversion 1.8, due out next year sometime.

jammycakes
  • 5,780
  • 2
  • 40
  • 49
6

You have heard right, sort of.

Git operates on the contents of the files, not the files themselves, so renames are technically meaningless to it. To git, a rename looks like file A disappeared and file B appeared with the same content as A. But git is actually pretty good at figuring out when a file has actually been renamed.

Try it: rename a file, then run 'git rm oldname' and 'git add newname' to tell git to stage the changes, then run 'git status' to see what git thinks it's doing -- you'll see that it tells you the file has been renamed. I'm not sure that means anything later, though. Look at a commit with 'git show ' and you won't see any mention of a rename, just a bunch of lines removed from one path and added to another.

Alternatively, you can also use the 'git mv' command to rename a file. It doesn't change how git sees the operation, it just effectively does 'mv oldname newname', 'git rm oldname' and 'git add newname' in one step.

For an overview, of Mercurial, see tonfa's answer.

SVN, on the other hand, cannot detect renames but must be told about them with the 'svn mv' command. When told, however, it tracks renames as "first class" changes, so when viewing the changelog later you'll see that the change was a rename.

I wouldn't suggest choosing a SVN over git or mercurial based on this feature, though. There are much larger and more important differences between the tools. I'd first decide whether you want a distributed version control system (git or mercurial) or a centralized version control system (svn).

Community
  • 1
  • 1
divegeek
  • 4,795
  • 2
  • 23
  • 28
  • 1
    it is not intented to be a shootout. I am using all of these tools, merucrial beeing used as my prefered tool for personal projects where i have a choice. – Johannes Rudolph Oct 08 '09 at 13:42
3

One more thing about git that hasn't been mentioned yet, in addition to using heuristics to determine whether a rename has happened:

If a file or even an entire directory tree is renamed, copied, or moved, and nothing underneath is modified in any way, then the file or tree is actually stored as the same object inside the repository, and doesn't take up any extra space.

If you modify it, then it's stored as a new object, as usual.

I'm not sure about hg and svn, but I suspect their changelist-oriented architectures mean they behave differently in this scenario. It doesn't really have any effect on usage, except it might give you reason to avoid moving or copying huge trees inside your repository.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
0

git tracks contents, not files. i’m not sure about mercurial, but svn has to be explicitely told about renames

knittl
  • 246,190
  • 53
  • 318
  • 364