Sunday, February 24, 2013

Git tips and tricks

http://www.openlogic.com/wazi/bid/268619/git-tips-and-tricks


You only need a basic command of Git to make the source code management utility incredibly useful. But Git offers a range of commands and options that can make your workflow run even more smoothly. Here are a few Git tips and tricks to make this tool even greater than you already thought it was.

Aliases in git

There are undoubtedly a few Git commands that you use regularly – git checkout, git commit, and git status, for example. You can reduce the number of keystrokes you type to invoke them by using Git aliases. Git stores its aliases (and some other config options; see below) in the file ~/.gitconfig. You can edit this file to add alias lines:
[alias] 
    st = status 
    co = checkout 
    cm = commit 
Or you can add them with a command like this:
git config --global alias.st "status"
To see what aliases and other config options you have set, run the command git config -l. Aliases are particularly handy for some longer commands, such as some of the commands discussed below. One useful alias is ds = diff --stages, which shows you a diff of the changes you're about to commit to the repository – once they're added, but before you commit. It's good practice to cast a quick eye over those before you commit.

Visualising and managing git branches

If you have lots of branches, it can be useful to see a graphical representation of your Git history. One option for this is gitx, either in its standard version (which should be included in your Git install by default) or using this improved fork. However, if you just want to see a graphical representation of your history on the command line, try
 
git log --graph --simplify-by-decoration --pretty=format:'%d' --all
simplify-by-decoration shows only commits referred to by a branch or tag, and the only output here is a ref name (%d; see the PRETTY FORMATS section of the git log manpage for more format info). A more verbose version shows all commits, with their subject lines, author, date, and abbreviated commit hash, in pretty colors:
 
git log --graph --pretty=format':%s %Cblue%an, %ar %Cgreen(%h)%Creset'
Add --simplify-by-decoration to that last version to get a cross between the two – simplified, with only certain commits, but with more commit info.
Once you've had a look at a tree, you may notice that you have a lot of branches that seem to have already been merged. This is especially true if your workflow makes a lot of use of Git branches. To find out which branches have been merged into other branches, try git branch --merged, or use git branch --no-merged to show the unmerged branches. You can then manually delete merged branches with git -d. (Thanks to Chris Kelly for this tip.)

See more Git log history

git log is useful for reviewing your commit history, but it's quite basic. It shows only commit ID, author, date, and commit message. I often want to also know which file was changed. You have a few options for doing this:
  • git log --stat: gives pretty output showing which files changed and how many changes were in each (additions and deletions). Unfortunately, though, it does not show added files.
  • git log --name-status: shows files changed and whether they were added, modified, or deleted, but doesn't show the nature of changes or the number of changes overall.
  • git log --name-only: just shows commit info and names of files involved, but not what happened to them.
Using these you should be able to get the information you want.
You can also use the git whatchanged command, which shows the commit logs and diff output for each commit. The default diff output is Git's internal format; for a textual diff, which is more useful to a human reader, try git whatchanged -p, though this is usually overkill if all you want is to check which files were changed.

Ignoring files

To ignore files globally across all your git repositories; edit ~/.gitignore:
git config --global core.excludesfile '~/.gitignore'
If you accidentally add a file you'd rather have ignored, you can unstage it from the commit with git rm:
git rm --cached unwantedfile.txt
You can then add it to the local or the global .gitignore file to ignore it in future.

Amending and reversing changes

You may sometimes commit something you didn't want to, or commit too early. Try running git rebase -i if you wind up making multiple commits that should have been all in one, or if you wish to consolidate a bunch of commits for code review. Make sure your changes are up-to-date and that you're on the branch you've been working on, then type git rebase -i master to squash the commits you've made on that branch into one commit on the master branch. You'll see a screen that looks a bit like this:
 
pick afa7532 Array handling methods 
pick b860afa Reading from notebook file into array  
pick df1af7b Setting up notebook file methods

# Rebase cb44197..df1af7b onto cb44197
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
You can reorder the lines if you like, so for example you could edit the above file like so:
 
pick afa7532 Array handling methods 
squash df1af7b Setting up notebook file methods 
pick b860afa Reading from notebook file into array 
This leaves you with two commits (array handling and notebook file methods, and reading from file into array). Or edit it like this:
 
pick afa7532 Array handling methods 
squash b860afa Reading from notebook file into array  
squash df1af7b Setting up notebook file methods
to squash all three into a single commit. Once you save the file and exit the editor, the changes are committed to the master. If you change your mind before that, delete all the lines and nothing will happen. You can also of course rebase against another (non-master) branch.
You can amend a commit with git commit --amend. Make changes in the local copy of the codebase that you would like to commit (for example, maybe you forgot to add foo.rb, so type git add foo.rb), then type git commit --amend. foo.rb will then be included in the last commit (along with everything that was already there), and you'll have a chance to edit the commit message. If you make no local changes and just type git commit --amend, you get a chance to amend the commit message. Alternatively, if you want to keep your commit message and just amend the commit, use git commit --amend -C HEAD.
To undo a commit altogether, use git reset --soft HEAD^. This undoes the last commit but leaves the changes staged. git reset --hard HEAD^ undoes the commit and deletes all your changes locally; use this with caution!
A slightly different way to approach an individual commit is git cherry-pick. This command enables you to pick a specific commit and add it to your branch, without also taking all the other commits with it. This can also be useful if the branch you're cherry-picking from is so out of date that it would be hard to merge it all in with your own branch. Check out the branch you wish to add the commits to, find the commit ID from the other branch, and use:
git cherry-pick COMMIT_ID
If you run into conflicts, you need to fix them in the normal way.

Patches, or adding changes in bits

Sometimes you might be working on several unrelated changes, and wish to commit them separately. You can do this on a per-file basis either by using git add this.file and working through the list, or git add -i to go into the interactive version of git add, which allows you to pick changes and stage untracked files.
You can also use the patch functionality to commit individual chunks of changes to a file. Type git add -p myfile.rb to be walked through the various chunks of changes that you've made to myfile.rb, and be given the option to add each to the commit. ? gives you help on the command options once you're in there. (Again, thanks to Chris Kelly for this one.)
Check out the Git man pages for yet more options and tricks!

No comments:

Post a Comment