-3

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
jww
  • 97,681
  • 90
  • 411
  • 885
  • 1
    What do you get from `git status`? – Amit Sep 23 '16 at 17:54
  • What was the exact sequence of commands you executed on the original machine? – Scott Weldon Sep 23 '16 at 17:55
  • @Amit - At the machine that performed the move: ***`Your branch is up-to-date with 'origin/master'.`*** However, I deleted the clone and re-cloned. I found its required on occasion to keep Git from breaking its index files. When that happens, I can't even switch branches, so its a state I try to avoid at all costs. Copies and moves seem to regularly break Git, so I do it after the operation. – jww Sep 23 '16 at 18:06
  • I didn't understand that whole comment (other then the output of course), but I wanted to know what you get at the "other" machine – Amit Sep 23 '16 at 18:10
  • 1
    @ScottWeldon - the command history was added. it was taken from Bash's `history` command. – jww Sep 23 '16 at 18:16
  • @Amit - Sorry about that. The `git status` you were provided was (1) at the machine where the moves occurred; and (2) provided *after* a sequence of `rm -rf ; git clone ; cd `. – jww Sep 23 '16 at 18:18
  • So after a clean clone, you get the files in the original location *and* the new location, *and* there are no modified/added files?? – Amit Sep 23 '16 at 18:20
  • @Amit - ***The problem at the new machine***: the old files remain. I performed a move (the old files are deleted, the new files are created), not a copy (the old files remain, the new files are created). ***The problem at the old machine***: When I attempt to perform a merge, it will fail. Git will get into a state where its index files are broken, and I won't be able to do basic things like switch branches. Its happened so many times in the past the `delete ; re-clone ` is part of my process when working with Git. I refuse to continue fighting with the tool. – jww Sep 23 '16 at 18:22

2 Answers2

3

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

Scott Weldon
  • 9,673
  • 6
  • 48
  • 67
  • Thanks Scott. How do I make Git behave as expected? I want `git mv` to actually do what nearly everyone in the free world expects when they move a file. – jww Sep 23 '16 at 18:26
  • @jww Updated my answer. – Scott Weldon Sep 23 '16 at 18:28
  • Thanks Scott. I think we are suffering a disconnect (due to my ignorance; my apologies). I don't need to rewrite history or anything fancy. Trying to do fancy and clevers thing just complicates my life and takes away from my primary task. That's why I delete and start over. For future reference, when I perform `git mv ...; git commit ...; git push`, how do I ensure a move occurs and not a copy? The best I can tell Git only copies; it does not move. Is there a *"real"* move command that actually moves the file? – jww Sep 23 '16 at 18:36
  • @jww No problem. As mentioned in my answer, as long as you do a plain `git commit` (leaving out the filename), you should be fine. I've updated my answer to clarify how this works. Does that make sense now? – Scott Weldon Sep 23 '16 at 18:47
2

The problem is in your (ab)use of the git commit command.

git mv does move the file as it should. Say, I have a repo with a single file, called a, and I want to move it as b:

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

    renamed:    a -> b

$ ls
b <-- Only b exists here, no a

The move is now recorded in the index, but not committed. To create the commit, I do:

$ git commit -m "Move a as b"

However, what you did was:

$ git commit b -m "Move a as b"
[master b275677] Move a as b
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b

When you give a filename as argument to git commit it only includes the files listed in the commit, instead of recording the current index.

Internally, git does not understand what a move is. It understands that a file was created, and another file was removed. The tools that are used to display history (git log for example) combine these two pieces of information and display the file as moved. But when you specified that only the newly created file should be included in the commit, it did not record the deletion of the old file, so it does not appear as a move when you pulled from the other machine.

So the answer to your question: "Why does 'git mv' not move a file? How to make it act “normally”?" - it DOES move a file, but you specifically told git to commit only the creation of the new file, not the deletion of the old one. To make it act normally, don't do that - instead, issue git commit without filenames as arguments.

1615903
  • 32,635
  • 12
  • 70
  • 99
  • Thanks. *"you specifically told git to commit only the creation of the new file"* - actually, I told Git to move a file; not add or delete. What it does behind the scenes is its business. After the move, there is only one file on th file system (re: the `ls` commands). That 's the one I commit. If Git left both files on the file system, then I would tell git to do something with both. Is there any way to get Git to act normally? – jww Sep 27 '16 at 14:13
  • 1
    Yes, you told git to move a file, and it did. But then you told it to not commit the whole move, but only commit the creation of the new file. To make it "act normally", don't do that. – 1615903 Sep 28 '16 at 04:36