Search code examples
gitshellterminalmove

Using git mv and avoid fatal source/destination error


The fact git mv is used for both moving/renaming is causing some problem. My current status looks like this:

wireless-10-104-40-80:UMD-LabVIEW-Boot-Camp deepayanbhadra$ ls
AND Gate.lvproj             Integer Detector.lvproj
Account Balance.lvproj          Integer Parity Detector.lvproj
Basics                  LICENSE
Coin Toss.lvproj            Palindromic Word Detector.lvproj
Convert Temperature.lvproj      README.md

Here, Basics is a folder/directory and the rest are files. I want to move all the files into the Basics folder. After consulting some SO posts, I tried this and got an error:

wireless-10-104-40-80:UMD-LabVIEW-Boot-Camp deepayanbhadra$ git mv Coin Toss.lvproj Basics
fatal: bad source, source=Coin, destination=Basics/Coin

There must be a really simple way to do this. Yet, as a beginner, I'm stuck.


Solution

  • This has nothing to do with Git itself, and everything to do with your file names vs the shell.

    When you enter commands at the command line in the shell (the line ending with $ before you start typing), the shell itself breaks up the command into a series of argument words using white-space as the default:

    $ word1     word2 word3    word4
    

    for instance becomes just four words, regardless of the amount of space between each word.

    This vector of four words is then treated as a command, with the first word being the name of the command and the remaining three as arguments. If the command were, say, echo, it would simply print back each of its arguments:

    $ echo    A           B C         D
    A B C D
    

    Note that echo prints one space between each word. If the command were, say, wc, the wc command treats each argument as a file name, and tries to open that file:

    $ wc A B C D
    wc: A: No such file or directory
    wc: B: No such file or directory
    wc: C: No such file or directory
    wc: D: No such file or directory
    0 0 0 total
    

    If you wanted to count words in a file named Coin Toss.lvproj and ran:

    $ wc Coin Toss.lvproj
    

    the wc command would see two separate arguments: Coin, and Toss.lvproj, and try to open two separate files. Neither file exists so the command would fail the same way it does with A B C D.

    You must convince the shell to send, as one single argument, the string Coin Toss.lvproj. There are many ways to do this; the simplest is to use quote marks:

    $ wc "Coin Toss.lvproj"
    

    Here the shell sees the quote marks and knows that the space should not be used to separate arguments. The shell removes the quote marks and passes the (single) argument Coin Toss.lvproj to the command.

    The git command takes, as its first argument, the sub-command to run. The sub-command mv then takes one or more source file names and either a destination directory name, or (if given one source file name) a destination file name. So once again, you must protect the blank in the file name Coin Toss.lvproj, which you can do the same way:

    $ git mv "Coin Toss.lvproj" Basics
    

    You could protect each word:

    $ git "mv" "Coin Toss.lvproj" "Basics"
    

    and the shell would dutifully make sure that mv is one word (removing the quotes) and that Basics is also one word, but that's not necessary since neither word contains white-space.

    (This is why file names with embedded white space are not used so much on Unix-like systems: they are inconvenient as they require some kind of quoting when using the shell.)

    There are many other forms of quoting available in shells, including using a backslash prefix:

    $ git mv Coin\ Toss.lvproj Basics
    

    and some operations, such as using file-name completion, will do this automatically.