Hacker News new | past | comments | ask | show | jobs | submit login
Git add -p: a powerful git feature (johnkary.net)
194 points by joshuacc on Nov 5, 2012 | hide | past | favorite | 107 comments

Or you could just use a GUI app and save yourself a lot of time. SourceTree (OS X only, free - http://sourcetreeapp.com) has changed the way I use git since I started using it a few weeks ago.

The best thing is the discoverability - you don't need to know about the -p flag, for instance, since it's handled by the UI. Same for many other useful git commands that are hard to remember - they are often a simple option on a right-click menu, or a checkbox when performing an operation.

I never advocate GUIs as the primary means by which a developer works with version control. Sure it might be OK for some things like browsing history (GitX) but most GUIs often abstracts away much of the underlying complexity, which is absolutely great if you're a designer without much command-line experience, but absolutely a disservice to your in-depth knowledge as a developer and the tools you rely on every day.

If you write code day in day out you SHOULD be comfortable on the command-line. That's why I highlighted in the article that I need two tools to do my development: an editor and version control. You use these tools every day--EVERY day. There's no excuse for not knowing them intimately. They are the foundation for doing your job.

GUIs also aren't always available across platforms, either. For the most part, all the git CLI commands port from Mac, Linux and Windows (msysgit). Also, have you ever tried using a GUI when SSH'd into a remote server in a pinch? You can't. And you'll really wish you invested the time to learn those underlying CLI commands.

Do remember that half of all developers are below average/median for the relevant group. (See also "perpetual intermediates".)

My experience has been in forcing developers to use guis to do commits. It significantly improved their commits because they would browse over the changeset, could commit only specific hunks, didn't accidentally include extra gunk like test files, and generally made better messages.

It is certainly the case that the cli tools give experts the most power. But that power doesn't need to be exercised by everyone, and not even by experts on every occasion.

I would much rather my developer's heads are full of things relevant to the problem being solved (which is what pays the bills) than intricacies of cli tools.

What does a graphical diff and GUI message editor with interactive changelist management have to do with being "below median"? I came to write code, not restart my workflow from scratch every time I want to slightly edit my change.

Because everyone thinks of themselves as being above median, and a wiz at the tools. They know all the flags, workflows etc memorized and think everyone should be like them.

Whilst in the real world the other half do things like 'git commit -a' to make commits which likely picks up unwanted gunk. A gui does not require memorizing flags and makes everything plain.

I think denying beginners the GUI is particularly cruel. The command line is much more of an abstraction in this case, I think! - gitk displays the actual tree of this graph that git manipulates, but the command line just prints text ;) It's especially illuminating (or I found it so, anyway) to keep gitk --all open as you do merges, rebases, pushes, fetches, etc., refreshing the display after each operation - it makes it very clear what's actually happening. I found git a bit of a mystery until I realised I could see what it was doing this way, and suddenly everything became clear.

Or maybe that's just me. I don't claim to be the sharpest tool in the box.

But anyway, you need have no fear that people won't get forced into using the command line eventually, just because they like git gui. Any time you want to do anything remotely complicated, you need to drop out of git gui and hit the command line. gitk, for all its ability to display the history in a useful format, is similarly limited (or designed for a specific purpose, if you prefer).

I really didn't get on with GitX, but I don't remember it being a great deal more than git gui and gitk, in the same app, with a fancy OS X-looking interface. So the same would probably apply.

Both gitk and the cli are representations of the graph. Granted, gitk may be a more useful/comprehensive overview than say git log but still.

In general I agree. In the case of git, I disagree. The git CLI is downright terrible and dangerous to use unless you're already a git pro, I would never ever force a git newbie to stick with the CLI instead of using a GUI like SourceTree (which is fantastic).

I've used CVS, Subversion and Mercurial in the past and never needed or wanted a GUI, but with git, I don't even want to be bothered by all the inane commands and switches. I love git for all it does, but I despise the CLI because it simply is downright evil: inconsistent, incoherent, convoluted, allows you to shoot yourself in the foot in too many ways, terrible user messages, and so on.

The git CLI is downright terrible and dangerous to use unless you're already a git pro,

The Unix CLI is far more dangerous than Git. Also, there not very much you can do really wrong if you regularly push to a remote repository, as long as you don't push with force.

And don't we all do backups anyway?

>> The Unix CLI is far more dangerous than Git. Also, there not very much you can do really wrong if you regularly push to a remote repository, as long as you don't push with force.

Sure, but that's not really an excuse for git to have such a confusing and possibly destructive command line. I'm not a GUI person at all, but the times I had to lookup 20 different ways that 'git reset' worked, or had to google what on earth the apparently random combination of words like refspec, remote, origin, branch and non-bare actually meaned have been enough to drive me to a GUI tool. Now I'm much happier and actually enjoy working with git. Obviously when you are only pulling and pushing from master, there's not much that can go wrong, but if that's all you do, Subversion would do the trick just as well. The CLI usability completely falls apart when you start rebasing, merging and cleaning up WIP commits.

A tool like Mercurial proves that DVCS systems do not have to have a confusing CLI, in all the years I've been using it, I've never felt compelled to use a GUI. Too bad the Mercurial back-end is so slow.

3 comments up I predicted 'reset' would make an appearance. What is the point of having multiple command names if the command name says nothing about its behavior, and the command flags trigger completely diverse functionalities? Just have the CLI be git -a to git -zz and stop teasing users.

> The Unix CLI is far more dangerous than Git.

Not necessarily: the Unix CLI has more powerful ways to saw off your leg, but it won't saw off your leg by accident, it won't destroy your files because you passed a strange flag to `ls`.

Mostly in agreement, I never said one shouldn't learn the git CLI and the fundamental concepts - I did that myself. But once you've mastered that, you're ready to move on to something that speeds up your day to day work.

Needing to use git now and then while SSH'd in is not a good reason to make your entire development process slower and more error-prone. I would also say that there is a larger problem with your process if you have the need to use git on "a remote server in a pinch".

It's kind of like learning to play a musical instrument. You absolutely must pay attention to the fundamentals as a beginner, but if you only ever work on the fundamentals you will never become a great musician.

this is one of the cases where the gui [i like git-cola personally] is definitely a better way to do things. if you complain that it won't work "in a pinch", you're optimising for the uncommon case. yes, it's useful to know how to do it from the command line, but for day-to-day use i find the gui more efficient.

I feel GUIs can be great learning tools to become better with the CLI. I've personally learned a lot of tricks and commands using a GUI Git client.

Save time with a GUI? Seriously???

Keyboard strokes are, by any definition, much faster than mouse pointing-and-clicking. I have yet to find a single GUI that gets stuff done faster than what I can achieve with a command line prompt.

How about staging 8 files out of 10 that are currently modified, with no matching glob patterns for the CLI to use? I don't care how fast your keyboarding is, simply selecting the 8 (even better, select all and unselect the 2 you don't want) and dragging them into the index is the fastest way.

Or how about firstly determining which old commit you would like to revert a single file to, then secondly doing the actual revert. I can just view the file's log at a glance, get the revision I want, right-click and select "Reset File to This Commit".

By the way, I love Vim. But your disdain for "pointing-and-clicking" sounds like so much machismo.

git add -i

Great tip, didn't know that!

Some people literally actually don't have a mouse. It gets that good.

These people can use the keyboard - git has comprehensive command line support, even if it isn't always as quick as git gui.

They day I learned that gvim has support for mouse hover tooltips, was a great day.

I can't imagine myself using a GUI for git, but there are some things that are faster with a GUI and mouse than with a keyboard. Almost all of them involve some kind of visual feedback. For instance, lately I've been working on some projects that involve absolutely positioning HTML elements on the page. This is a huge pain using a text editor--you make an estimate, save it, reload the page, see how it looks, and repeat. Just clicking and dragging is much, much easier.

Other than that, I generally think that keyboard is a faster input, though an 80-character terminal window is often a poor choice for visual feedback. Termkit has some interesting ideas: http://acko.net/blog/on-termkit/

For the future, mnutt: I love Chrome's developer tools, you can directly edit CSS rules from the browser by right clicking on it and selecting inspect element. Helps a lot with these situations!

And it's a gui!

TermKit author got mad at the community last year and went underground. :-( No idea if it will ever launch.

Yes, I use magit to stage chunks in emacs. Even if I have modified a file and end up staging the whole thing, looking at what i'm staging is way more useful than the way stock git presents things.

Your working speed is limited by typing or clicking? Mine is limited not by the speed I output information, but by the speed I get input. GUIs excel at that. A graphical view of source history is much better than what you get from the CLI, which is like living life while looking through a straw.

SourceTree is really cool but IME it's best for beginners to start with the command line. I've taught git to quite a few people now and see a correlation between early command line usage and understanding what is going on.

Once you're at the level of git add -p you can probably start thinking about using a GUI if that's your cup of tea.

Or just use gitx (or git-cola on Linux, or git gui if there's nothing better) and get a much better visual reference and easy of use. Not everything needs to be done from the command line.

At least for OS X, SourceTree is way better than any flavour of gitx. It provides a very nice interface for `git add -p`; I use it several times daily to craft tidy commits.

"gitx (l)" is much nicer and more complete than the old unmaintained gitx, but it has a number of bugs and casually, awful memory leaks.

I don't ever use git -p directly, but I use magit (https://github.com/magit/magit) for emacs, which makes staging changes this way very easy.

Lately I find myself launching an emacs session just so I can use magit for projects I build in XCode.

It makes crafting commits a joy.

+1 emacs users should definitely try magit for this. I use the git CLI a ton but never for interactive staging at the hunk / sub-hunk level.

I Always use -p.

I can't count the number of times I've caught stuff that wasn't supposed to be committed.

Also, site isn't loading for me so maybe this is discussed, but I also like using it to not commit logging / debugging statements without removing or stashing them.

For anyone using Mercurial, the hgrecord extension distributed with hg gives you the same feature - http://mercurial.selenic.com/wiki/RecordExtension

I can't remember the last time I used a naked commit; record has become part of my core workflow.

"git add -i" is my favorite command to start the interactive mode to add multiple files to staging at once.

John Kary's page is down, but he created two screencasts, which are the meat of his post:



Sorry everyone, thought we had Varnish setup. I'll do my best to get it back up. My screencasts above are what I'm mostly linking to.

There's a cache of the article here: http://hncache.bensbit.co.uk/4744405?textonly=1

Every time I mention this on HN, at least 15 people upvote it - which means they didn't know about it. So I feel obliged to repeat it again and again (as I usually use this method a few times a week and it's tremendously useful for me):

If you want to get Google's cached version of a webpage, just type

    e.g.: cache:http://johnkary.net/git-add-p-the-most-powerful-git-feature-youre-not-using-yet/
in the search bar and press return.

EDIT: 14 already, after an hour!

There's also 'git reset -p' to interactively unstage changes

And git checkout -p to interactively bring over parts of another commit (or interactively revert uncommitted changes, if you're checking out HEAD).

Git's "add -p" ui is great. If you're stuck on another VCS you might want to check out http://porkrind.org/commit-patch/ which, through the "commit-partial" command, lets you review and edit your patch before committing. It works with many of the open source VCSes out there.

I tend to use it even for git just so I have a consistent interface no matter what project I'm working on.

Yeah, this is a great tip. It's great for a final review prior to actual commit. Often, I'll build my commit message based on my walkthrough at that point. Additionally, you get cleaner diffs because you can skip over goofy stuff like whitespace changes (which you then can destroy after the commit by a quick git checkout HEAD myfilewithwhitespacechangesinit)

But I am using it!

But I am usin git?

I do this but with git gui, it's much easier this way: http://i.imgur.com/3Mrwr.png

Site wont load for me.

I'm a big fan of "git gui". You can commit individual lines.

Here's another article discussing git add -p: http://blog.tplus1.com/index.php/2008/10/31/break-up-changes...

Nice. Too bad I'm on svn. Though I suppose you'd want to be really careful about not creating a commit that's broken, e.g. by overlooking a declaration. Can you easily check whether currently staged changes still compile and/or pass a test?

yes, using the -k option of git stash allow you to 'put away' the changes which are not in the index.

The argument of creating a broken commit is natural, but not very relevant IMO: after all, with svn and co, the risk of forgetting to commit a new file is pretty strong as well. If you care very much about having non-broken commit, the only reliable solution is a gateway that merge commits only after they pass a test suite.

Highly recommend git-svn for committing from git locally to svn remote

Even if you are on svn, you can still use git. The svn bridge works quite nicely.

Agreed. It's a triviality but git add -p is one of the main reasons my commits in Git are cleaner than in SVN. It's also one of the advantages I cite when advocating for Git.

If you haven't tried it, I highly recommend taking a look at Stacked Git (stg). It adds a beautiful layer on top of git that helps you manage a "stack" of commits. Useful when you're working on a large change and want to think about smaller commits.

Combined with `git add -p`, stg is easily the most useful git tool in my toolbox.

It's an absolutely fantastic (though dangerous) command. It's also a good illustration of how git doesn't commit anything you don't want to see committed (eg, "git commit" doesn't do anything if you haven't done "git add" before, letting you prepare your commits more carefully than with other VCSs).

You really need to test the version of the code you are going to commit. See the comment about using "git stash" elsewhere in this thread.

You need to test the version of the code you are going to publish, and unless you are publishing every time you commit, you don't need to test each commit.

I use git add -p whenever I've made too many changes between commits. I like for my commits to be of a single topic if possible.

If I use git add -p to break up a commit into 10 separate chunks, and find out something is broken, it's not hard to find the commit that caused the issue (if the commits are topical and not mixed).

Yes, but I think that's missing the point. It's routine to have lots of modifications to a code base at once -- the big one being extra logging or debug code floating around after a fix. At that point, you have to do a separation task where you prune out all the needless stuff.

That's what this is for. And it works pretty well. Any other helper for this task would look pretty much the same -- the goal after all is to translate your local hacks into a clean commit.

So you end up committing untested code? (since you did test the whole change, not just part of it).

Sounds like a bad idea to me.

None of this implies committing untested code. The index is not a commit; it is a pre-commit staging area. When you use 'git add -p', you are adding hunks to the staging area, not committing them.

What's the difference? When you're done staging your patch, you can then check out the staging area and run unit tests on it prior to promoting it to a commit. Doesn't compile? Tests fail? Restage as necessary.

Really? I'd definitely commit before running tests. You can always use reset to redo the commit later. Branches are cheap, commits are cheap.

I usually do something like:

  git add -p
  git commit
  git stash
  <run tests>
If it fails then I can stash pop and commit -p --amend and try again.

With stash -k you can test without committing first. Although git is very flexible regarding this, I prefer to at least test the code before committing it.

`git checkout-index` allows you to run tests on the index before committing and without messing with your working tree. The only downside is that it might take a while for a large project. Example script: https://github.com/philc/vimium/blob/master/git_hooks/pre-co...

When you're done, just use Bernhardt's "run-command-on-git-revisions" to run tests on each of the commits to ensure they are all working code.


Nothing wrong with committing untested code, it's pushing it as a ref one can reasonably expect people will merge/fast-forward to that it becomes a problem. git add -p is a nifty workaround for the type of people who don't like the workflow of "commit early, commit often" (some people have 'save file' bound to also make a commit) but who still want to have lots of small, contained commits.

Not a huge deal, but it can become problematic when you're using something like git bisect.

So what? Just test the commit after you've made it. You can always reset and try again before pushing.

A lot of times I use "git add -p" to cleanup code, e.x. if I added some extra logging statements I don't want to commit I can go through adding everything except the log statements, then reset everything that hasn't been added to the index.

git stash -k; make test; git stash pop, git commit;

Problem solved.

The problem is with the process, people will forget. You probably need to automate this.

If people forget to test their commits, they're going to forget them without using git add -p too.

test: stash-keep-index

Committing untested code is an excellent idea. You should do it several times a day. Pushing untested code to a shared branch on a public remote is a bad idea.

That is most definitely a major issue of this technique, though it can probably be mitigated through various hooks.

I find this technique great to keep unrelated fixes in separate commits. A single revision may not pass a set of unit tests, but the entire feature branch will before being merged into the release line. Keeping the barrier low for committed code is one of git's best features.

> Keeping the barrier low for committed code is one of git's best features.

When I first learned git, I was told, 'You'll never have to comment out code again'. Keeping the barrier for commits low (along with git -p and git rebase -i) makes this a reality.

> A single revision may not pass a set of unit tests, but the entire feature branch will before being merged into the release line

I really don't care a whit about your feature branch when your broken commits mean I can't bisect an issue.

There's also git add -e, which I find to be more powerful as it gives you the ability to choose the context of the changeset you're going to be staging by glob.

I wrote a blog post about it here: http://pivotallabs.com/users/khicks/blog/articles/2116-git-a...

There's also git reset -p, which is also in the same realm as git add -e as far as how powerful/advanced it is, though perhaps more difficult to comprehend. I wrote a blog post on that one, too: http://pivotallabs.com/users/khicks/blog/articles/2114-git-r...

You mean there are people who do NOT always use -p with add?

I guess you learn something new every day. I thought this was the only way people use git and I was actually surprised to find out it wasn't made the default.

When preparing a series of commits in a branch, what is the recommended method for editing non-HEAD commits? I know how to git commit --amend the HEAD commit, but how would one modify an earlier commit and rebase/replay the subsequent commits? git checkout the earlier commit and create a new branch from there?

In Mercurial, I would use an hg patch queue to hg qpop, qrefresh, then qpush.

"git rebase -i" is what is commonly used instead of hg patch queues. You pass it the name of the last unchanged commit, and it opens an editor where you define what operations to do on it : fold commits together (fixup, like qfold), stop for editing a commit, reorder them, change their description, etc.

Thanks for the tip!

You're welcome !

I still feel like detailed commit messages and fine grained commits are just too much busy work. I think that there must be a better, more automatic way. I'd love to start with a system that just let you easily drag back and forth history. I feel like the future may well still be built on top of git, but I don't think that we need to be so meticulous with our code.

I'm going to guess that you mainly use version control for your own projects or in a small team of 2.

While the idea of a "Time Machine" style history is probably quite desirable when working on your own, it's totally useless for practice of checking in self contained "features", having them reviewed and potentially rolling them back even after many other "features" have been added that you want to keep.

I alias icommit="!git add -p && git commit -e -v" since i normally do the two together.

commit has gained a `-p` flag "recently".

I may have to update my alias then

One thing I don't like about it is that you often can't keep splitting chunks down to the line level. I usually end up having to type 'e' to edit the patch manually.

Yes, my main grievance with git add -p as well; someone fix this.

It might be my eyes, but the video is too blurry to be of much use. A couple of paragraphs would have been preferable.

tig and git add -p are my standard way of using it. Sometimes from within vim(yeah you can use fugitive, but it has its' own purpose.

beats any gui imho, although tig is technically a gui.

for those that don't know it, compile: http://jonas.nitro.dk/tig/

and type 'S'

OT: I'd love an interactive "Learn Git" webpage where you could learn to use Git with demo scenarios. Something like "Try Ruby" but for git.

You didn't look very hard then :] There is a course made by the same people who made Try Ruby, on the same site. It's named, unsurprisingly, 'Try Git':


They also have a second, more advanced course on the site.

:( Wow. I failed pretty hard at looking clearly. Thanks to you and the others for the links.

Here is exactly what you're looking for: http://www.codeschool.com/courses/try-git

Then you should check out http://try.github.com/

All you need to do is type "apt-get install git-core" or "brew install git" and you can try 95% of git out locally. Get a free bitbucket or github account and you can do most of the rest. :)

OK, perhaps you are on Windows, then i am not sure if it's that simple.

That's not even kind of all it takes.

I'd been using git for a couple months on a project on my own with no problems. I felt comfortable with it. Then I got hired at a company using it.

I wasn't even remotely prepared for the difference (and for how little I actually knew about git: addressing commits, modifying commits to make them more clear to review, moving branches around, etc). I flailed for several weeks (coworkers were always happy to help, but I prefer to try to swim on my own) before the light bulb really went off (and I'm not new to VCS or DVCS, eitherm by any stretch of the imagination).

I don't think you can really learn git until you are working with a group of people, because its not until that point that its strengths and peculiarities really show up. Especially since the UI is so cryptic.

Fwiw, I was a big mercurial fan pre-hiring. I'm now very happy that BitBucket has a git interface (I prefer BB to Github for closed-source, personal projects) as, unless there is a requirement that history not be changed in the repo, I'll be using git moving forward.

Step 2: write some code that exercises all git features

That was more what I was looking for.

"Oh, you're friend was 20 commits behind and did [some stupid] shit that wiped out all commits and GUI tools aren't helping? Here's how to attack it..." which is where my Git knowledge falls apart very quickly.

Regular commiting/pushing/remotes etc are all okay to me, except the bad habits of using "sync" as a commit message when I move from my desktop to my laptop and I don't have wip branch.

Check out the Peepcode screencast on Git: https://peepcode.com/products/git

Peepcode screencasts are worth every penny.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact