Wednesday, February 11, 2015

How to Git rm a file with special characters in the name?

Git GUI tools like SourceTree and the GitHub client are great.  But there are plenty of times when the command line is exactly what you need to get the job done.

In particular if you go looking on the 'net for tips on fixing particular problems or carrying out operations that you don't do very often, you'll usually find the answer as a sequence of command lines to type out.

Also sometimes you are on a remote server via ssh and using git to fetch some project or deal with your web project managed via git.  In these cases you won't have you graphical client.

Well, here's a good git trick:  git add -i

~/depot/SpaceBotAlphaUpdate $ git add -i
           staged     unstaged path
  1:    unchanged        +0/-0 SpaceBotAlpha.spritebuilder/Icon

*** Commands ***
  1: status   2: update   3: revert   4: add untracked
  5: patch   6: diff   7: quit   8: help
What now> 2
           staged     unstaged path
  1:    unchanged        +0/-0 SpaceBotAlpha.spritebuilder/Icon
Update>> 1
           staged     unstaged path
* 1:    unchanged        +0/-0 SpaceBotAlpha.spritebuilder/Icon
Update>> ^D
updated one path

*** Commands ***
  1: status   2: update   3: revert   4: add untracked
  5: patch   6: diff   7: quit   8: help
What now> q
Bye.

~/depot/SpaceBotAlphaUpdate $ 


This command invokes the interactive form of git add, which allows you to select the files you want to add for update or as new files (which were previously untracked).

In this case SpriteBuilder had previously created a bogus file of zero length.  The last two characters in the name were unprintable and were being displayed in the console as a question mark.  The file looked like "Icon?" or "Icon/r" depending on what tool was used.

SourceTree was not able to process the file - I could select it but could not git rm it.  Finder was the same.  I could select but not delete it.

The following command allowed me to remove it from the shell:

rm -- Icon*


Here the -- marks the end of switches to the shell so that everything after -- is treated as a file name. When the shell expands the Icon* into the non-printing characters, the -- ensures that those characters aren't interpreted as switches or other controls to rm. So it was gone from the file system. Now how to remove it from source control?

The above git add -i session was the answer.

After invoking git add -i you get a basic status listing.  Here in the first line its showing that a 0 length file is currently changed in the working set (the unstaged column) but is unchanged in the cache of files about to be committed.

I type a '2' to enter the update sub-menu, where again I see the same line of output.  Typing a 1 here chooses the file listed with index 1 to be updated in the cache index of files for commit.  If the file had been modified by changing the code contents this would have added that file to the commit.

The real power comes from adding large numbers of files by typing for example

1-33,45-62

...where you select two ranges of files, and exclude the others.  Very handy for dividing large change sets up into one or more commits by selecting only the changed files you want.

Note the typing of control-D (end-of-file) to tell add -i to pop back from the sub-menu.  Here if you want you can go into the add untracked menu by typing either 'a' or '4'; to put new previously untracked files under source control.

Once you're all done, control-D out to the top menu, and do a final status with 's' to check you have what you want.  Now pop out of git add -i altogether with a 'q' for quit and type

git commit -a "My amazing changes"

to finalize the commit.