SH (3) [Avatar] Offline
#1
I'm working my way through Chapter 7 and I've reached the Try It Now on page 80. Listing 7.6 shows the output of the git status command after using the git mv to rename the second file in the renaming example. I have tried stepping through the example a couple of times and I always get a different (and confusing) result.

Listing 7.6 shows the correct output:
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
# 
#       renamed:    c -> renamed_file
#       renamed:    d -> another_rename
# 


However, after following the steps in the book my output looks like this:
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
# 
#       renamed:    c -> another_rename
#       renamed:    d -> renamed_file
# 


I read the help for the git mv command, which didn't address this. I then did a little bit of testing and found that if there is some text in one of the files, making them different (in the example files c and d were both empty), the rename worked as I would expect.

Is this behaviour a result of the files being the "same" and git mv, when it determines if a file is being renamed, simple picks the first file with the matching content or something like that?

Thanks
rickumali (130) [Avatar] Offline
#2
Hello! Thank you for commenting about the book on the forums.

I reviewed listing 7.6 and tried to recreate it. One of the difficulties is that the example builds from the previous chapter. To make the listing easier to reproduce, I isolated it into its own example in the attached BASH script that you can examine and run on your environment. It will create a directory list76 that will make a repo whose git status matches listing 7.6.

It should make no difference whether the file contains text or whether the file is empty. The git mv command works the same in either case. In our example, both c and d are empty, but Git knows which one is renamed to what file.

Run the script, and take note of the output. Go into the created "list76" directory, and then do git status, and then "git ls-tree --long HEAD". The output will be:

% git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    d -> another_rename
        renamed:    c -> renamed_file

% git ls-tree --long HEAD
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    c
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    d


The ls-tree output is most interesting. You're right that c and d are empty, so they have the same contents (the e69de2 'blob'), but when you commit the renames match up properly. To test this, try git commit.

I avoided talking about the internals in this case, but Pro Git has a good chapter on it at:

https://git-scm.com/book/en/v2/Git-Internals-Git-Objects

Let me know if this helps or is confusing. Ultimately, your environment should match listing 7.6, despite the fact that we're renaming empty files.

Finally: it _is_ possible that things could have changed in Git. I ran my test with Git 2.5. Let me know what version you're at.

Thank you!
SH (3) [Avatar] Offline
#3
Thanks for the reply, Rick. I ran your shell script on both Windows (git version 2.11.0.windows.1) and Linux Mint (git version 1.9.1). Here's the output:

Shawn@i7-3770K MINGW64 /f/Documents/GitBook/test
$ bash make_mv_files.sh
Initialized empty Git repository in F:/Documents/GitBook/test/list76/.git/
[master (root-commit) 5449ccd] Adding c and d first time
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c
 create mode 100644 d
-------------
Git Objects
Use git show <SHA1D> to display the object
.git/objects/54:
total 1
-r--r--r-- 1 Shawn 197121 136 Jan 16 09:30 49ccd1e7fe96e174d21d9b775db6e65f765840

.git/objects/59:
total 1
-r--r--r-- 1 Shawn 197121 51 Jan 16 09:30 2be6b15ea6d7060cfcc646a780253c2e7b20d7

.git/objects/e6:
total 1
-r--r--r-- 1 Shawn 197121 15 Jan 16 09:30 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
-------------
Git Tree
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    c
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    d
-------------
Rename c to renamed_file
-------------
git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    c

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        renamed_file

no changes added to commit (use "git add" and/or "git commit -a")
-------------
Remove c with Git
rm 'c'
Add renamed_file to repo
-------------
git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    c -> renamed_file

-------------
Rename d to another_rename with Git
-------------
git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    c -> another_rename
        renamed:    d -> renamed_file


Shawn@i7-3770K MINGW64 /f/Documents/GitBook/test/list76 (master)
$ git ls-tree --long HEAD
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    c
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391       0    d

Shawn@i7-3770K MINGW64 /f/Documents/GitBook/test/list76 (master)
$ git --version
git version 2.11.0.windows.1


The final git status reports the same for both, with c -> another_rename and d -> renamed_file. The discussion about the internals is a little beyond my understanding to be helpful, unfortunately.
rickumali (130) [Avatar] Offline
#4
Thank you for letting me know your versions! I neglected to try the script on my Windows Git. I ran make_mv_files.sh, and it produced the correct listing. However, my version was 2.8.0.windows.1, which is quite dated. I upgraded to 2.11.0.windows.3, and reran the script. Lo and behold, I encountered the same issue as you did!

What were your results on Linux Mint? I see from your reply that your Linux is version 1.9.1, which should mean that you won't see the error there.

I feel that this is a regression of some kind, but a mild one (albeit confusing). I rely on the touch command to quickly make files, but it does expose users to the corner case that two empty files are really the same object in Git's eyes. I'm glad you noted that just having content in these files allows you to generate the correct listing. I'll update the errata to alert other readers to this issue.

Thank you for commenting!
SH (3) [Avatar] Offline
#5
I'm sorry I forgot to mention that the results of running your script on Mint (git v1.9) were the same as Windows. I have since upgraded git on Mint to v2.11 which also, not surprisingly, gives the same results.

Simply adding the line echo "This is file c." >> c after the touch c d to your script and re-running it produces the "correct" results.