Search code examples
gitgit-mv

git: rename file and create file with old name in same commit


In my experience, when I rename a file, git will detect the rename on my next commit. However, now I want to rename a file, say from old_name to new_name, and in the same commit, I want to create a new file named old_name. Since a (different) file named old_name exists, git is not automatically detecting that this is, in fact, a rename. Is there a way to force git to detect this rename?

So far, I have not been using git mv because I hear it is almost the same as renaming via any ordinary method, but would this help in my situation?

I can see that git is not detecting the rename by running commit -a --dry-run; it lists it as a new file and a modified file.

Even if I am able to force the commit to detect the rename, will this cause any issues down the road when I try to track code across renames e.g. via git log --follow?


Solution

  • There isn't really any such thing as a rename in Git. Git knows nothing of the concept of renaming a file. No information about any such thing is built into a commit or anywhere else. The use of git mv is totally irrelevant to the matter.

    What Git does do is try to be helpful to you, the human, when doing a diff between two commits, by noticing if two files with different pathnames seem to be "the same file" — that is, a file is deleted and a file is created, and they seem to have very similar contents.

    So if you want to help Git help you, make a commit consisting entirely of renaming the file. Then make another commit where you create a new file with the old name. Thus when Git walks the parent chain, one commit at a time, it will stand a better chance of drawing the "right" conclusion at every step.

    In general, however, you should not count on this behavior on the part of Git.

    Here's an example, where I renamed a to c in one commit, and then created a new a in another commit. I'll show excerpts of the log listing so that you can see what Git thinks happened in each commit:

    % git log -p
    
    diff --git a/a b/a
    new file mode 100644
    index 0000000..b680253
    --- /dev/null
    +++ b/a
    
    diff --git a/a b/c
    similarity index 100%
    rename from a
    rename to c
    
    diff --git a/b b/b
    new file mode 100644
    index 0000000..6178079
    --- /dev/null
    +++ b/b
    
    diff --git a/a b/a
    new file mode 100644
    index 0000000..7898192
    --- /dev/null
    +++ b/a
    

    As you can see, reading backwards, Git thinks I performed the following steps:

    1. Create a.
    2. Create b.
    3. Rename a to c.
    4. Create a.

    And that is exactly what I think I did too, so Git is "helpful" in the desired way here.