This is something a bit like git-add -i, except not depending on perl, and partially crossed with git-commit. This file is divided into two main sections: an introduction section and a reference section. The introduction section is intended to give someone unfamiliar with the program some idea what it does; the reference section gives the full details for someone already at least mildly familiar with it. Introduction Usage: $0 [arg [arg ...]] Runs git diff with the same arguments. Any non-text diffs, new files, or deleted files cause an immediate error out. Text diffs are accumulated; each diff hunk is remembered separately. The user interface allows the user to, repeatedly, choose from: - Choose a diff hunk to be included. - Commit chosen hunks. - Split hunks. Splitting consists of dropping into a text editor to edit the diff hunk. You are assumed to know how diff hunks are constructed and are expected to manually turn the hunk into a intermediate diff hunk. That is, if the diff hunk goes from state A to state Z, and you want to commit a hunk that's in state M, you have to manually convert the A->Z hunk into an A->M hunk. git-interactive will then generate an M->Z hunk for you. (You can then subsequently split either of the resulting hunks further if you want, and/or re-merge splits. But see below.) Committing works by constructing a git-fast-import pack for the new commit, then running git-commit --amend to let the user supply (edit, really) the commit message. If the commit --amend is aborted, the commit created by git-fast-import is backed out. (There are lots of race conditions with unpleasant consequences if other commits are happening to the same branch at the same time. This is not intended for workflows that do that.) There is a potential issue when splitting hunks: the portions have to be committed in order. This is because, when splitting, the second part of a split is relative to the output of the first part. Since arbitrarily complex changes can be introduced when splitting, it is not in general possible to automatically figure out what the diff corresponding to the second part of a split would be if applied to the initial contents, rather than to the output of the first part. Thus, this isn't really fixable even in principle. Since split hunks can be merged and re-split, you can re-split it so the part you want to commit next is first; it's annoying to have to, though, so be careful to split in the correct direction. The UI is curses-based. You move the cursor up and down with j and k (to move by lines) and f (or space) and b (to move by screens). J and K move by "large" amounts, which usually means "the next line of the type you're on", but when within a hunk it moves to the appropriate end of that hunk. l expands the file or hunk the cursor is on; h collapses (if a file or hunk is expanded), or moves to the containing object (if not). a ("apply") toggles whether a hunk is included in the next commit (indicated by a > marking); C commits the marked hunks. S splits a hunk, letting you edit the diff with your favourite editor; the resulting diff becomes one part of the split, with the remaining changes becoming the other. m allows you to merge split hunks, though you can merge only adjacent pieces (for, basically, the same reason mentioned above that prevents committing arbitrary pieces of a split hunk - it's impossible, in general, to figure out what the diff would be to apply a part of a split to something other than the result of applying all the previous parts). To merge two parts of a split, type m on one of them, then on the other; if you change your mind after the first m, type m on the already-marked one to cancel the merge. You can't do splits or commits while a hunk part is merge-marked. The Makefile is unlikely to work for you out of the box, but this is arguably a feature, because (because of the way I have it set up) it installs a copy into ../git-interactive, which is likely to not be what you want. If you want to use the Makefile, you'll need , from (eg) ftp.rodents-montreal.org:/mouseware/local-src/makefiles/. But there's really no need; just compile the .c files and link with -lcurses -ltermcap. You will need lcs-cvt (or a patched compiler) to handle four of the seven files (git-intf.c, memstream.c, subproc.c, and ui.c); see ftp.rodents-montreal.org:/mouseware/local-src/lcs-cvt/. Reference The curses screen conceptually has a window onto a list of lines. There is one line per file; mentioned in git diff otuput; each file line can be either collapsed (in which case just the file line appears) or expanded (in which case it has hunk lines within it, one per unapplied hunk - a file with no unapplied hunks is not displayed). Similarly, hunks can be collapsed or expanded; when expanded, the lines of the diff hunk are displayed. Hunks retain their status (expanded or collapsed, marked-for-application or not, etc) even when the file they belong to is collapsed. Keystroke commands: j, k Move down (j) or up (k) by one line. J, K Move down (J) or up (K) by a `large' amount. This usually moves to the next line of the kind you're on (on a file line it moves to the next file up or down, for example, skipping over any non-file lines in between), but, if you're on a line that's displaying part of a diff hunk, it moves to the end of the hunk. h On a diff line, moves to the hunk line for the hunk it's part of. On an expanded hunk line, collapses the hunk. On a collapsed hunk line, moves to the file line it belongs to. On an expanded file line, collapses the file. On a collapsed file line, does nothing. l In a loose sense, the inverse of h: on a collapsed file line, expands it; on an expanded file line, moves to its first hunk line; on a collapsed hunk line, expands it; on an expanded hunk line, moves to its first diff line; on a diff line, does nothing. m Marks a hunk for merging. If the hunk the cursor is on or in resulted from a split (see the S command), marks it for merging. When you mark two adjacent hunks split from the same original hunk for merging, they are collapsed into a single hunk. You cannot merge hunks not from the same original hunk and you cannot merge non-adjacent hunks even if they are split from the same original hunk - see the introduction section for why. A hunk marked for merging is indicated with an `m' in the left margin. a On a file line, does nothing. Otherwise, marks the hunk the cursor is on/in for application, or, if it's already marked, unmarks it. Hunks can be marked or unmarked independently of other hunks; hunks marked for application are indicated with a `>' in the left margin. C Commits marked hunks. This constructs a commit that includes just the marked hunks, then runs git commit to let you enter a commit message. If you abort the commit (by giving an empty message), anything already done is backed out. If the commit succeeds, the marked hunks disappear from the display and you can continue to manipulate any remaining hunks. If you do a commit that leaves no uncommitted hunks, git-interactive exits. S Splits a hunk. This takes the hunk as text and drops you into $EDITOR (default "vi") to edit that text. (Actually, it uses $GIT_EDITOR if that's set, like git.) You are expected to take the A->Z hunk and convert it into an A->M hunk for some intermediate state M; git-interactive will then compute an M->Z hunk. On the display, the hunk you split will become the hunk you created in the editor, with a second hunk below it holding the rest of the pre-split hunk. If you edit the hunk into something inconsistent with the original state (an invalid hunk, or one whose "from" lines don't match the starting text), git-interactive will complain and give you the choice of either continuing to edit ("e-edit") or aborting the split ("iscard edits"). See also the `m' command. f (also space) Loosely put, moves down by one page. This actually finds a line 4/5 of the way down the screen (or the last line if that occurs first), then makes that the top line of the screen; if this would move the current line off the top of the screen, the (new) top line becomes the current line. b Loosely put, moves back by one page. This actually moves the top line to a point 4/5 of the way down the screen (stopping at the very first line, if that occurs first). If this would push the current line off the bottom of the screen, the last line becomes the current line. There is no `quit' command as such. If you commit all hunks, quitting is automatic; to quit before all hunks are committed, just use your interrupt character (most likely ^C). Hunk diff line text display is truncated at the right margin of the screen; doing anything else looked harder than I wanted to get into. Thoughts welcomed.