tags:

views:

6867

answers:

32

What are your favorite git features or tricks, or even workflows? Post one feature, trick, or workflow per answer.

+13  A: 

My favorite feature is interactive rebase back to the last upstream commit. This allows me to edit, merge(squash), and drop all of the commits I haven't pushed upstream yet, before I do so. Since I work on top of svn, my workflow looks something like:

git rebase -i svn/working_branch
<edit/squash/drop/etc>

This gives me a list of all of the changes I have on this branch that aren't in svn. I repeat the above as necessary, often doing squashes and reordering in multiple steps to reduce the impact of merges and keep myself sane. When I'm satisfied my commits are clear and coherent, then I do one final unit test run before I dcommit.

Scotty Allen
+23  A: 

The Git Magic article is an excellent reference for tips and tricks. I still go back and reread it occasionally to pick up new things that I missed the previous time I read it.

Greg Hewgill
+1 for git magic. it never gets old.
Abizern
url is not found now for Git Magic, can you update the post Greg ?
Rachel
@Rachel: It appears that the 404 error was only temporary, the url works now.
Greg Hewgill
@Greg: Thank you Greg, it works fine now.
Rachel
+42  A: 

git stash - Great for quickly parking what you're working on, switching to another branch to work on, and then going back to what you were doing. Saves making a commit (not that spurious commits are a problem in git thanks to git rebase -i and squashing)

git commit --amend - for those of us who have a terrible habit of committing before compiling/testing.

Abizern
I think the author means `git commit --amend` by "git amend". git amend is not a command in itself.
Ollie Saunders
Same applies to git squash. Is that referring to the squash feature of rebase?
Ollie Saunders
+31  A: 

My favourite feature, hands down, is the index. Since my initial surprise about it abated and I got used to it, I have wondered why anyone would want to work without this concept. The initial bewilderment about the behaviour of git diff with regards to the index turned into solid conviction.

Often when I start to do a particular thing in a codebase, I don’t have a clear idea up front about what I will want to do. With git, I just start working and see where the work takes me. I can use git add to incrementally add things for the next commit to the index, either as I work or afterwards, and use git diff to review which tentative changes I have not yet evaluated.

In that way, git lets me follow a pretty free-wheeling style of work, while still allowing me to easily render it as a series of small coherent patches once I have found out what made sense to do. I don’t need to plan ahead carefully and follow the plan meticulously.

Yeah, the index is an extra layer of indirection that seems unnecessary at first glance. What I have found is that instead it is liberating.

[Addendum: Over a year after this reply, I wrote In praise of Git’s index on my weblog, wherein I say essentially the same things, at length.]

Aristotle Pagaltzis
Fascinating. I use it occasionally, but never in a way as useful as that.
Jeremy Banks
It especially means you don't have to create a branch for each new feature/debug. You do your work, then just commit the bit that's related to a given change.
Benjol
Thanx for opening my eyes on this feature. The interactive adding is explained in more details at http://book.git-scm.com/4_interactive_adding.html
kraymer
Thanx for opening my eyes on this feature. The interactive adding is explained in more details at http://book.git-scm.com/4_interactive_adding.html
kraymer
Thanks for opening my eyes on this feature. The interactive adding is explained in details at http://book.git-scm.com/4_interactive_adding.html
kraymer
I think `git add -i` should be mentioned.
Josh
I never once used that. However, I use `git add -p` all the time. That didn’t exist at the time I wrote this answer, though, I think.
Aristotle Pagaltzis
+29  A: 

I tend to work on master optimistically. I'll occasionally need to retroactively create a branch to put my work off to the side. git makes this easy. I don't always have the same recipe, but it'll look something like this:

# Convert the recent work on master to a feature branch
git branch feature-branch
# Drop my master back to the same ref as origin
git reset --hard origin/master
# Switch to the new branch
git checkout feature-branch
Dustin
Isn't this somewhat equivalent to a stash? (Except that a stash doesn't touch the object database.)
mjs
Were you standing in master before the first command?What would happen if you skipped the reset part? (if you had commits on current branch - then I guess I understand).mjs: the stash is for throwaway changes, I guess he actually decides to keep things on the branch for some time.
inger
+3  A: 

To echo @Aristotle Ppagaltzis, the index allows you to do neat stuff like commit partial changes within a single file.

git add --interactive
Will Robertson
+87  A: 

git bisect

So in this C project I was working on recently, one of our regression tests began failing. The project was in an in-between state, so I figured something was just temporarily broken, and as we filled things back in it would probably pass again.

A good number of commits went by and things started to come together, but this old test was still failing. Clearly someone had actually introduced a bug along the line, rather than merely introducing a temporary hole in functionality.

So I run git bisect start, then git bisect bad on the experimental head of the repository. Then I jump back about thirty commits to find one where the test passes and I run git bisect good. git then jumps me to a commit halfway between the known good and the known bad commits, I run the test and do git bisect good or git bisect bad. Repeat this process about five times and I'm right at the commit where the bug was introduced.

I'd done a fairly innocent seeming cast of a pointer, which screwed up some pointer arithmetic, since you're so curious.

All in all it took just a couple of minutes. However, it turns out I did this the slow way. Since I had a test program that returned 0 on success and something else on failure, I could have simply given git the command to run it, and it could have found the commit in question in seconds. See: git bisect run

Peter Burns
+1 for a full and excellent example!That's why I like git. For everyday work the normal SCM workflow of add, commit, branch and the like are right at your fingertips. But there are plenty of esoteric things (like git bisect) that reward a look through the docs rather better than brute force.
Abizern
also present in mercurial as hg bisect
just somebody
Bisect is wonderful. And you can set a script to determine if a branch is good or bad, automating the whole process. Haven't tried that, but looking forward to...
Felixyz
+5  A: 
git checkout -b some-experiment
# do some work
git commit -m 'some feature'
# do some more work
git commit -m 'some other feature'
# experiment fails
git checkout master
# start working on new thing
git commit -m 'some mundane bugifx'
# realize I need some code from my experimental branch, but not the whole thing
gitk --all
# figure out the sha-1 of the change I want
git cherry-pick the_sha1_of_some_other_feature
davetron5000
The lack of a single quote after "'some other feature" is messing up the highlighting.
Andrew Grimm
you can right-click and cherry-pick directly in gitk, no need for the final command.
davr
Yes, but you can also use "git log --all" to find the sha1's instead of gitk. Doing this all in gitk would break the "workflow demo by CLI" davetron5000 chose to use.
phord
+25  A: 

git add --patch (or -p) and its big brother git add --interactive (or -i). They allow you to stage only certain chunks from all the changes you have in your working directory into the index, and you can even edit them, so your commit looks exactly the way you want it to.

cypher
Also, since 1.6.5, `git checkout`, `git reset`, and `git stash` all learned related --patch options.
Chris Johnsen
+15  A: 

Here's a neat trick:

Create a script named git-foo in your $PATH. When you run 'git foo', it'll call your custom script.

This is the easiest way to add commands to git and have them appear to be built-in.

The easiest way is to use aliases as explained in Ropez's answer.
spatz
I wouldn't call aliasing as adding commands. How would one e.g. implement adding hg-imitating command 'git addremove' using an alias? (Hint: it takes only two short lines of script using the technique mentioned by David.) I, personally, think this extendability is great, btw.
Jawa
+58  A: 

Show Branch Name in Bash Prompt

Great tip for working with git from the command line. Basically, you can set your Bash prompt to display your active git branch, if and only-if you are inside of a git repository. You can do this by updating the PS1 define or adding to the end of your .bashrc file.

PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '

The shell will now display the following prompt:

[user@host workingdir (master)]$
Casey
Nice! I've had to source git-completion.bash first in my .bashrc to make it work.
kraymer
+11  A: 

gitready.com is awesome -- lots of tip-of-the-day style tricks.

ramanujan
+23  A: 

Adding alias definitions using git config. Here's some that I often use:

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.staged 'diff --cached'
Ropez
+12  A: 

Simply knowing that git reflog exists, and will allow me to revert to any previous commit if something has gone wrong. It remembers those commits that was "deleted" by rewriting history, using commands like git rebase, git reset, and git commit --amend.

Show every commit that the master branch has pointed to in the past:

git reflog show master

Revert to the tenth commit in the list:

git reset --hard master@{10}

Start a new branch from the commit that the master pointed to one month back:

git checkout -b branchX master@{'one month ago'}

Maybe not the features that you use most often, but knowing that they exist allows you to use other features without worrying about destroying anything.

Ropez
Every time I see the word "reflog", I accidentally mis-parse it as "re-flog" rather than "ref-log". To flog someone again vs. a reference log.
Seth Johnson
If you wonder: no git doesn't save this data forever. Reflog entries older than 90 days are pruned by `git gc`.
kaizer.se
I was just saved by reflog, I'm kinda new to git. I had checked out a previous version (before an introduced bug) and wanted to bring back a few files from the later buggy version... but I didn't even know its ID anymore. Hooray for reflog!
Myrddin Emrys
+4  A: 

Just the basic fast and whenever-you-want branching. Having come from using svn, the ability to separate changes out from each-other and continue coding on two different branches effortlessly is priceless.

I use branches for my feature todo list, for bugfixes, for backups, and I love that the branches don't exist in a certain path, they're just waiting in an alternate dimension to be switched to at any time.

Tchalvak
I like for the same reasons, but I used the same approach under CVS, Clearcase and SVn. Except for the "effortlessly" part. ;) git definitely makes this easier.
Don Branson
Man, from what I remember of SVN branching, if you used branches for everything in svn, you're a glutton for punishment. *smiles* I remember branch merges being quite the pain. But then, I haven't used it for... ...say, two years. Wonder if it has gotten nicely better, I hope so.
Tchalvak
The main difference with SVN is that you had to manually tell it what revisions to merge. Once you did that, it was about as easy as git 98% of the time. Now supposedly SVN can figure out the revisions...but it doesn't always work.
davr
I recall that branches in SVN live on the server. That means you can't make a branch that's just local, knowing that you can throw it away later and the repo be none the wiser. That, more than syntax or time, is what makes SVN branching "heavyweight."
Wayne Conrad
+6  A: 

git blame is too verbose to my taste (although you can customize output format) I recently discovered git gui blame, which I find much easier to read.

I also love git cherry -v master, to see the local commits on a branch that haven't been pushed into master yet.

Diego Pino
+4  A: 

One of my favourite features of git (and mercurial, and probably other DVCS) is that I can just zip up my project's folder and I have a working repo with full history. No need to pull/push whatever (although I know I can do that), I just send a zip and voilá, the repo is there as well as its history.

Miguel Ping
dunno about git, but hg bundle is better than zipping a clone: it's just the repository, compressed with bzip2.
just somebody
+4  A: 

Add this short script to your .zshrc

autoload -Uz vcs_info

precmd() {
  psvar=()

  vcs_info
  [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_"
}

PS1="%m%(1v.%F{red}%1v%f.)%# "

to get your command line prompt to display current branch/tag you're on.

A bit more in-depth look into vsc_info here

Art
+3  A: 

Combination of git stash and git fetch. This applies for when you have a team of people working with a central repository (like gitorious or gitosis).

If I am working on something and then when I am ready to push the changes instead of just committing them I would do a fetch to see if the remote has changed. If it's changed I would stash, do git merge origin, then git stash apply. This avoids an automatic merge message when you pull on top of your commit (like Merge branch 'master' of <server>:project).

Igor Zevaka
+4  A: 

Quickly find the differences between your current branch and another one, like master:

git log master..

No need to give the current branch name.

+3  A: 

Reusing previous commit message when automatic merge fails. In fact git tells you use -c <SHA1> when you do merge and it fails.

After you fix up the conflicts all you do is this:

git commit -C <SHA1>

And the original commit message is retained.

Igor Zevaka
+8  A: 
git log --oneline --date-order --graph --all --decorate
yoda
In git v1.6.0.4 `--oneline` should be `--pretty=oneline`:git log --pretty=oneline --date-order --graph --all --decorate
RobM
The `git log` manpage says: `--oneline: This is a shorthand for "--pretty=oneline --abbrev-commit" used together.`
Daenyth
+23  A: 

The ability to rewrite history with "git rebase -i". One of the things I use it for is to commit my work as a series of small changes, but before I push it upstream, mash all the of the changes that belong together into one rev.

# (work work work)
$ git commit -a -m'WIP: Refactoring ftp fetcher to make bug fix possible'
# (work work work)
$ git commit -a -m'WIP: Added a test for passive mode bug'
# (work work work)
$ git commit -a -m'Fixed passive mode bug in ftp fetcher'

Now I've got three commits that I want to smash together into one before I push them upstream:

$ git rebase -i HEAD~3

Git brings up my editor with a file that starts with these three lines:

pick d56cc97 Fixed passive mode receive bug in ftp fetcher
pick 2c9b044 WIP: Added a test for passive mode bug
pick 5d87764 WIP: Refactoring ftp fetcher to make bug fix possible

I change the second and third "pick" to "s" (for "squash"), save the file, and exit my editor:

pick d56cc97 Fixed passive mode receive bug in ftp fetcher
s 2c9b044 WIP: Added a test for passive mode bug
s 5d87764 WIP: Refactoring ftp fetcher to make bug fix possible

Git smashes the revs together, then presents me with the comments

# This is a combination of 3 commits.
# The first commit's message is:
Refactoring ftp fetcher to make bug fix possible

# This is the 2nd commit message:

Added a test for passive mode bug

# This is the 3rd commit message:

Fixed passive mode receive bug in ftp fetcher

Which I replace with:

Fixed passive mode receive bug in ftp fetcher

And that's all.

Having this ability means I can commit any time I want a good "go back to" point and not worry about "polluting" the upstream tree with all of my nervous little commits.

Wayne Conrad
+4  A: 

I rather like git log -g, which shows the history of where the HEAD has been, even if you reset yourself up and down your commit set.

It's a bit like ORIG_HEAD but goes back for weeks – this has gotten me out of a mess many many times, and gives me confidence to play around with some of git's fiddlier features without having to tie on a safety branch before I start.

Alex Brown
That's pretty sweet. Never seen that one before. Much nicer than just `git reflog`. Thanks!
Dan Moulding
+1  A: 

Mine favourites are:

  • git cherry-pick
  • git blame -- a.file (faster than SVN and gives more info)
  • git gui and clicking files for staging into commit
Rafal Rusin
I use tig all the time. It's pure command line and not as clunky as `git gui`
Chirag Patel
A: 

Igor, Instead of using git stash, you can use git pull --rebase origin. This will rebase your local branch commits onto the remote changes.

Ken K
+1  A: 

I really use tig quite a bit. It's much quicker to use than git add -i. I can add files to be committed with 1 key in a single screen instead of adding 1 at a time. Also, it's much easier to see what has changed in your index. It's pure command line and not as clunky as git gui

Chirag Patel
A: 

Regarding the earlier comment , see the responses to this question.

lsiden
A: 

I'm partial, but the gbranch command in http://github.com/garysweaver/git-scripts allows you to track a remote branch if it already exists, make a remotely tracking git branch if one doesn't exist, or just switch to the local branch by that name:

gbranch (branch_name)

If you give it a second argument, it tracks a remote branch if it already exists, branches from the specified remote branch to make a remotely tracking git branch from it if the new branch doesn't exist, or just switches to the local new branch by that name:

gbranch (branch_name) (existing_remote_branch_to_branch_from)

For those that want to be lazy with Git, it works great, and I've been using it a while.

Gary S. Weaver
+7  A: 

git lg aliased to

git log --graph --all --pretty=format:'%Cred%h%Creset - %Cgreen(%cr)%Creset %s%C(yellow)%d%Creset' --abbrev-commit --date=relative

Łukasz Lew
looks great but there is an extra ' at the end ...
Ahmed Kotb
Fixed the extra '
Gerald Kaszuba
+4  A: 

GitHub + Schoolwork (branches for every homework assignment that eventually merge into master). 1 repo per class.

dougvk
Nice! I wish I knew about Git when I was in college. :D
jonasespelita