Archive

Posts Tagged ‘git’

git – how to easily remove ‘deleted’ files

September 7, 2010 3 comments

git is a distributed version control system. There are lots of tutorials online to teach you the basics of this great system; that’s not the intent of this post. Rather, I want to share a neat trick I found on another site.

When you move files that have been checked into git without using the git mv command, as often happens when using an IDE and renaming a file, you are left with untracked files, and deleted files.

Nick@Macintosh-3 ~/Desktop/git_example$ mkdir src
Nick@Macintosh-3 ~/Desktop/git_example$ touch src/Hello.java
Nick@Macintosh-3 ~/Desktop/git_example$ git add src/
Nick@Macintosh-3 ~/Desktop/git_example$ git ci -m "Initial commit"
[master (root-commit) 97eb204] Initial commit
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 src/Hello.java
Nick@Macintosh-3 ~/Desktop/git_example master$ git status
# On branch master
nothing to commit (working directory clean)
Nick@Macintosh-3 ~/Desktop/git_example master$ ls
src
Nick@Macintosh-3 ~/Desktop/git_example master$ mv src/Hello.java src/Goodbye.java
Nick@Macintosh-3 ~/Desktop/git_example master$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    src/Hello.java
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   src/Goodbye.java
no changes added to commit (use "git add" and/or "git commit -a")

As soon as you delete the src/Hello.java and add the src/Goodbye.java file, git is smart enough to realize that you really have just renamed or moved the file:

Nick@Macintosh-3 ~/Desktop/git_example master$ git rm src/Hello.java
rm 'src/Hello.java'
Nick@Macintosh-3 ~/Desktop/git_example master$ git add src/Goodbye.java
Nick@Macintosh-3 ~/Desktop/git_example master$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    src/Hello.java -> src/Goodbye.java
#

While this pattern is not too onerous when you move just a few files around, when you are in the midst of refactoring you could have multiple files moved into different directories, leading to a raft of these deleted files that need to be manually removed from git. Because they are no longer at the old location in the file system, we cannot use tab completion to help remove the old files; you must type out the full path to the file to be deleted. This can be a big pain.

For instance, imagine I have the following tree structure:

Nick@Macintosh-3 ~/Desktop/git_example master$ tree src/
src/
`-- org
    `-- example
        `-- nick
            |-- 1.java
            |-- 10.java
            |-- 2.java
            |-- 3.java
            |-- 4.java
            |-- 5.java
            |-- 6.java
            |-- 7.java
            |-- 8.java
            `-- 9.java

And we change the package name from an external, non-git process/program:

Nick@Macintosh-3 ~/Desktop/git_example master$ mv src/org/example/nick src/org/example/blog
Nick@Macintosh-3 ~/Desktop/git_example master$ git add src/org/example/blog/
Nick@Macintosh-3 ~/Desktop/git_example master$ git st
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   src/org/example/blog/1.java
#   new file:   src/org/example/blog/10.java
#   new file:   src/org/example/blog/2.java
#   new file:   src/org/example/blog/3.java
#   new file:   src/org/example/blog/4.java
#   new file:   src/org/example/blog/5.java
#   new file:   src/org/example/blog/6.java
#   new file:   src/org/example/blog/7.java
#   new file:   src/org/example/blog/8.java
#   new file:   src/org/example/blog/9.java
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    src/org/example/nick/1.java
#   deleted:    src/org/example/nick/10.java
#   deleted:    src/org/example/nick/2.java
#   deleted:    src/org/example/nick/3.java
#   deleted:    src/org/example/nick/4.java
#   deleted:    src/org/example/nick/5.java
#   deleted:    src/org/example/nick/6.java
#   deleted:    src/org/example/nick/7.java
#   deleted:    src/org/example/nick/8.java
#   deleted:    src/org/example/nick/9.java
#

It is a bit of a pain to remove all of the deleted files individually.

Fortunately, there is a shortcut.

Nick@Macintosh-3 ~/Desktop/git_example master$ git ls-files --deleted
src/org/example/nick/1.java
src/org/example/nick/10.java
src/org/example/nick/2.java
src/org/example/nick/3.java
src/org/example/nick/4.java
src/org/example/nick/5.java
src/org/example/nick/6.java
src/org/example/nick/7.java
src/org/example/nick/8.java
src/org/example/nick/9.java
Nick@Macintosh-3 ~/Desktop/git_example master$ git rm `git ls-files --deleted`
rm 'src/org/example/nick/1.java'
rm 'src/org/example/nick/10.java'
rm 'src/org/example/nick/2.java'
rm 'src/org/example/nick/3.java'
rm 'src/org/example/nick/4.java'
rm 'src/org/example/nick/5.java'
rm 'src/org/example/nick/6.java'
rm 'src/org/example/nick/7.java'
rm 'src/org/example/nick/8.java'
rm 'src/org/example/nick/9.java'

If this is a common enough use case, we can add aliases in the global ~/.gitconfig file to support this.

[alias]
    br = branch
    ci = commit
    co = checkout
    st = status
    # list files
    ls = ls-files
    # list deleted files
    lsd = ls-files --deleted
    # remove deleted files
    rmd = !git rm `git ls-files --deleted`

See the great wiki page on Aliases for information. The exclamation mark is used to indicate that the command invoked is a non-git one; you can execute arbitrary shell commands this way. For instance:

[alias]
    # A stupid thing to do, but illustrates that arbitrary unix commands can be executed in this manner
    cal = !cal
Nick@Macintosh-3 ~/Desktop/git_example master$ git cal
   September 2010
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

All credit to the git cheat sheet site that initially brought the ls-files –deleted trick to my attention. Hopefully the alias I have provided will be useful to some people.

Advertisements
Categories: git Tags: , ,

How to make git use TextMate as the default commit editor

July 21, 2010 2 comments
git config --global core.editor "mate -w"

Now when you do a git commit without specifying a commit message, TextMate will pop-up and allow you to enter a commit message in it. When you save the file and close the window, the commit will go through as normal. (If you have another text editor you prefer instead, just change the “mate -w” line to the preferred one)

For those curious what the -w argument is about, it tells the shell to wait for the mate process to terminate (the file to be saved and closed). Read this for more information about how to associate TextMate with various other shell scripts and programs.

Categories: textmate, Uncategorized, unix Tags: , ,