I’m sure that most “hacker bloggers” have probably done their own “N
Git Tricks,” post at this point. But git
is one of those programs
that has so much functionality and everyone uses it differently that
there is a never ending supply of fresh posts on this topic. My use of
git changes enough that I could probably write this post annaully and
come up with a different 9 things. That said here’s the best list right
now.
::: {.contents} :::
See Staged Differences
The git diff
command shows you the difference between the last commit
and the state of the current working directory. That’s really useful
and you might not use it as much as you should. The --cached
option
shows you just the differences that you’ve staged.
This provides a way to preview your own patch, to make sure everything is in order. Crazy useful. See below for the example:
git diff --cached
Eliminate Merge Commits
In most cases, if two or more people publish commits to a shard
repository, and everyone commits to remote repositories more frequently
then they publish changes, when they pull, git has to make “meta
commits” that make it possible to view a branching (i.e. “tree-like”)
commit history in a linear form. This is good for making sure that the
tool works, but it’s kind of messy, and you get histories with these
artificial events in them that you really ought to remove (but no one
does.) The “--rebase
” option to “git pull
” does this
automatically and subtally rewrites your own history in such a way as to
remove the need for merge commits. It’s way clever and it works. Use
the following command:
git pull --rebase
There are caveats:
- You can’t have uncommitted changes in your working copy when you run
this command or else it will refuse to run. Make sure everything’s
committed, or use “
git stash
” - Sometimes the output isn’t as clear as you’d want it to be, particularly when things don’t go right. If you don’t feel comfortable rescuing yourself in a hairy git rebase, you might want to avoid this one.
- If the merge isn’t clean, there has to be a merge commit anyway I believe.
Amend the Last Commit
This is a recent one for me..
If you commit something, but realized that you forgot to save one file,
use the “--amend
” switch (as below) and you get to add whatever
changes you have staged to the previous commit.
git commit --amend
Note: if you amend a commit that you’ve published, you might have
to do a forced update (i.e. git push -f
) which can mess with the state
of your collaborators and your remote repository.
Stage all of Current State
I’ve been using a versing of this function for years now as part of my download mail scheme. For some reason in my head, it’s called “readd.” In any case, the effect of this is simple:
- If a file is deleted from the working copy of the repository, remove
it (
git rm
) from the next commit. - Add all changes in the working copy to the next commit.
git-stage-all(){
if [ "`git ls-files -d | wc -l`" -gt "0" ]; then; git rm --quiet `git ls-files -d`; fi
git add .
}
So the truth of the matter is that you probably don’t want to be this
blasé about commits, but it’s a great time saver if you use the
rm
/mv
/cp
commands on a git repo, and want to commit those changes,
or a have a lot of small files that you want to process in one way and
then snapshot the tree with git.
Editor Integration
The chances are that your text editor has some kind of git integration that makes it possible to interact with git without needing to drop into a shell.
If you use something other than emacs I leave this as an exercise for the reader. If you use emacs, get “magit,” possibly from your distribution’s repository, or from the upstream.
As an aside you probably want to add the following to your .emacs
somewhere.
(setq magit-save-some-buffers nil)
(add-hook 'before-save-hook 'delete-trailing-whitespace)
Custom Git Command Aliases
In your user account’s “~/.gitconfig
” file or in a per-repository
“.git/config
” file, it’s possible to define aliases that add bits
of functionality to your git command. This is useful defining shortcuts,
combinations, and for triggering arbitrary scripts. Consider the
following:
[alias]
all-push = "!git push origin master; git push secondary master"
secondary = "!git push secondary master"
Then from the command line, you can use:
git secondary
git all-push
Git Stash
“git stash
” takes all of the staged changes and stores them away
somewhere. This is useful if you want to break apart a number of changes
into several commits, or have changes that you don’t want to get rid of
(i.e. “git reset
") but also don’t want to commit. “git stash
”
puts staged changes onto the stash and “git stash pop
” applies the
changes to the current working copy. It operates as a FILO stack (e.g.
“First In, Last Out”) stack in the default operation.
To be honest, I’m not a git stash power user. For me it’s just a stack that I put patches on and pull them off later. Apparently it’s possible to pop things off the stash in any order you like, and I’m sure I’m missing other subtlety.
Everyone has room for growth.
Ignore Files
You can add files and directories to a .gitignore
file in the top
level of your repository, and git will automatically ignore these files.
One “ignore pattern” per line, and it’s possible to use shell-style
globing.
This is great to avoid accidentally committing temporary files, but I also sometimes put entire sub-directories if I need to nest git repositories within git-repositories. Technically, you ought to use git’s submodule support for this, but this is easier. Here’s the list of temporary files that I use:
.DS_Store
*.swp
*~
\#*#
.#*
\#*
*fasl
*aux
*log
Host Your Own Remotes
I’ve only once accidentally said “git” when I meant “github” (or vice versa) once or twice. With github providing public git-hosting services and a great compliment of additional tooling, it’s easy forget how easy it is to host your own git repositories.
The problem is that, aside from making git dependent on one vendor, this ignores the “distributed” parts of git and all of the independence and flexibility that comes with that. If you’re familiar with how Linux/GNU/Unix works, git hosting is entirely paradigmatic.
Issue the following commands to create a repository:
mkdir -p /srv/git/repo.git
cd /srv/git/repo.git
git init --bare
Edit the .git/config
file in your existing repository to include a
remote block that resembles the following:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = [username]@[hostname]:/srv/git/repo.git
If you already have a remote named origin, change the occurrence of the word remote in the above snippet with the name of your remote. (In multi-remote situations, I prefer to use descriptive identifier like “public” or machine’s hostnames.)
Then issue “git push origin master
” on the local machine, and
you’re good. You can us a command in the following form to clone this
repository at any time.
git clone [username]@[hostname]:/srv/git/repo.git
Does anyone have git tricks that they’d like to share with the group?