Search code examples
gitgit-mv

Why does 'git mv' not move a file? How to make it act "normally"?


Please forgive my ignorance here... Here's the background: I created a TestScritps directory to organize test scripts. I moved three scripts from <root dir> to <root dir>/TestScripts. I moved one at a time and performed a local commit after each one. I then pushed all the changes.

I went to another machine and performed a pull:

$ cd cryptopp/
$ git pull
remote: Counting objects: 25, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 25 (delta 11), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (25/25), done.
From https://github.com/weidai11/cryptopp
   2ac9ea1..2a22a84  master     -> origin/master
Updating 2ac9ea1..2a22a84
Fast-forward
 TestScripts/cryptest-android.sh |   44 +
 TestScripts/cryptest-ios.sh     |   40 +
 TestScripts/cryptest.sh         | 5729 +++++++++++++++++++++++++++++++++++++++
 rijndael.cpp                    |    2 +-
 setenv-android.sh               |   85 +-
 5 files changed, 5870 insertions(+), 30 deletions(-)
 create mode 100755 TestScripts/cryptest-android.sh
 create mode 100755 TestScripts/cryptest-ios.sh
 create mode 100755 TestScripts/cryptest.sh

$ ls *.sh
cryptest-android.sh  cryptest.sh     setenv-android.sh   setenv-ios.sh
cryptest-ios.sh      rdrand-nasm.sh  setenv-embedded.sh

Notice the files were only copied; they were not moved.

I checked the git-mv man page, but the errant behavior does not appear to be discussed.

I have two questions. Why does git mv only copy files, and not move them? How can I make git mv perform "normally"? Here, "normally" means what nearly every person who has used the command line expects - it moves the file from <target location> to <destination location>.


Here is the relevant command history.

  994  rm -rf cryptopp/
  995  git clone https://github.com/weidai11/cryptopp
  996  cd cryptopp/
  997  mkdir TestScripts
  998  git mv cryptest.sh TestScripts/
  999  ls *.sh
 1000  git commit TestScripts/cryptest.sh -m "Organize test scripts (Issue 303)"
 1001  ls *.sh
 1002  git mv cryptest-ios.sh TestScripts/
 1003  git commit TestScripts/cryptest-ios.sh -m "Organize test scripts (Issue 303)"
 1004  ls *.sh
 1005  git commit

Solution

  • From the summary shown in the output of git pull, I see that those files were not deleted.

    When you did e.g.:

    git commit TestScripts/cryptest.sh -m "Organize test scripts (Issue 303)"
    

    While Git is usually good about tracking files moves, internally a move is recorded as deleting one file and creating a new identical one. You committed the new file, and not the file deletion from the original location, thus making it look like Git just copied the file.

    In the future, it's generally a good idea to leave out the filename from the commit command:

    git commit -m "Organize test scripts (Issue 303)"
    

    You can do git status beforehand to see what will be committed, and modify as needed.

    A full session might look like this:

    $ git mv cryptest.sh TestScripts/
    $ git status
    On branch master
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
            renamed:    cryptest.sh -> TestScripts/cryptest.sh
    
    $ git commit -m "Organize test scripts (Issue 303)"
    $ git status
    On branch master
    nothing to commit, working directory clean
    $ ls cryptest.sh
    ls: cannot access cryptest.sh: No such file or directory
    $ ls TestScripts/cryptest.sh
    TestScripts/cryptest.sh
    

    To fix, do:

    git rm cryptes*.sh
    git commit
    

    If you want to fix your history instead, you can do:

    git rebase -i HEAD^3
    

    and change the command for the relevant commits from pick to edit. At each stop, do:

    git rm <file>
    git commit --amend
    git rebase --continue
    

    Where <file> is the current original file.

    Note that this will rewrite your history, so it may cause problems when moving between machines.


    Note that it should not be necessary to delete and re-clone a Git repo, so if you run into trouble in the future, it's better to try to fix the root issue. (Search around or ask here if you need help!)