An healthy git repository, is readable and tracable. We need fully clear what we commit. The Git Index, is what we need, just like a shopping cart. We pick goods(updates) from the shelves(work tree). Then push the cart(index) to cash register, and buy(commit) them. With the Git Index, we have ability to deal with a big heap of updates orderly.

Basic Commands

   There are three git commands for managing Git Index. They are add, reset and checkout.

  • add add updates from work tree to index
  • reset remove updates from index
  • checkout give up updates that not be added to index (cover updates in work tree by index)    Both reset and checkout are multi-purpose. The command reset also can use to move current branch. And the command checkout also can use to move HEAD and checkout to the work tree. The difference is the parameter for the command. If the parameter is a commit(hash or branch, tag…), it’s for the branch and HEAD. If the parameter is a path, or there is no parameter, it’s for the index.

Meticulous operations: --patch

   Each of add, reset, checkout can run with option --patch, or -p in short. Git will iterate over the updates and let’s handle them one by one. You can run with paths as parameters to iterate over one file or several files, or just run with no parameter to iterate over all updates. Using there command with -p option is the best way currently to manage the Git Index. I name it 3Ps Sorcery.

   After run the command, we get a interactive interface like this:

diff --git a/index.md b/index.md
index 921ba51..1c0d98b 100644
--- a/index.md
+++ b/index.md
@@ -3,5 +3,5 @@
 
 
 layout: home
-list_title: foo
+list_title: bar
 ---
Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 

The last Line is different with different command:

add
Stage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 

reset
Unstage this hunk [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 

checkout
Discard this hunk from worktree [y,n,q,a,d,k,K,j,J,g,/,s,e,?]? 

   As we see, git show an update in the file index.md. And give us several operations y,n,q,a,d,k,K,j,J,g,/,s,e,?. What are they use for? By the first sight, I only know the ? provides helps. So we input ? Enter. We get the message:

add
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
reset
y - unstage this hunk
n - do not unstage this hunk
q - quit; do not unstage this hunk or any of the remaining ones
a - unstage this hunk and all later hunks in the file
d - do not unstage this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
checkout
y - discard this hunk from worktree
n - do not discard this hunk from worktree
q - quit; do not discard this hunk or any of the remaining ones
a - discard this hunk and all later hunks in the file
d - do not discard this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

   What’s a hugging heap of dazing stuffs! Don’t worry, just let’s reorder them:

handlers:
`y` yes       do (add/reset/give up);
`n` no        don't (add/reset/give up);
`a` all       do    for all in this file;
`d` don't     don't for all in this file;
`e` edit      see next section;
`s` split     some times, git will assume several updates as one, you can split it;

navigations (only works in a file):
`g` goto      goto the nth update, such as `g 1`,`g 2`, the number start with 1;
`/` search    just like searching in vim, less... It's convenient for a big file with lots of updates;
`j` next      next unprocessed;
`J` Next      next update;
`k` previous  previous unprocessed;
`K` Previous  previous update;

others:
`q` quit      quit;
`?` help      print help;

   We only need know y, n, s, q for basic using. For advanced using, we need know one more: e. For programmers, that’s enough. For dictionary keeper, or Webpack configuratician, navigations will be useful.

   In this section, we talk about the e. To begin the editing, we need an editor. Just like when you run commit without -m opiton, git will open vim or nano when you using e. And the text in the editor will like this:

# Manual hunk edit mode -- see bottom for a quick guide
@@ -20,4 +20,5 @@
 	><ul
 		><li>foo</li
-		><li>bar</li
+		><li>baz</li
+		><li>qux</li
 	></ul
# ---
# Here will be some manual text.

   Here is an update, we delete the line bar, and insert lines baz and qux. The line @@ ... @@ shows the line number and line counts before and after edit. And follows the content of update. The first character of each line is the line type, can be space, - or +. The space means this line is a normal line, a - for a deleted line, and an + for a new inserted line. Attention, this space is not indentation. If you use indent characters for indentation, here is a space at the first. If you use spaces instead of indentation, one more space each line.

   If you just quit the editor without edit anything, it’s just equal to press a y instead of e. If you recover the update, remove all + lines, change -s to spaces, it’s just equal to press a n instead of e. We can modify it as you like, but not all modifications are allowed.

   It’s easier to understand when we run add. Here is an example, if we make modifications like this:

 	><ul
-		><li>foo</li
+		><li>qux</li
 		><li>bar</li
+		><li>Baz</li
 	></ul

As we see, we don’t delete line bar, but delete line foo. And we edit line baz, and insert qux to another position. That’s not difficult. We’ll found that space lines and - lines can change to each others, but you can not modify their content; + lines can make any modifications, remove or insert anywhere when add.

   But when reset, it’s different. Oppositely, space lines and + lines can change to each others, but you can not modify their content; - lines can make any modifications, remove or insert anywhere when add. It’s confusing. So, example again. the same situation, let’s edit the text to this:

 	><ul
+		><li>foo</li
-		><li>Bar</li
+		><li>baz</li
 		><li>qux</li
-		><li>ET</li
 	></ul

To understand what will happen, we must using words like give up or cancel. We change line foo from space line to + line, means we cancel the adding of line foo, but the line foo is not added, it’s over there original, so it’s means delete the line foo in fact. We change line bar to line Bar, means don’t cancel the deleting of bar, but cancel deleting of line Bar. And means delete line bar and add line Bar in fact. At last, insert a line ET. If you still hard to get it, just assume + lines as - lines and - lines as + lines.

   On checkout, since the direction is same with reset, so reverse + and - too. What in the paper is abstract. For fully understand, you need try it by your self.

   There is a feature lack that, the add -p doesn’t with new files. However, reset -p dose. For the new files, you can use add filename then reset -p filename to add it with patching.

File level and global operations

   Usually, always check over what you change with -p is fine. But some times you need file level or global operations.

  • git add file add all updates in the file from work tree to index
  • git reset file remove all updates in the file from index
  • git checkout file give up all updates in the file that not be added to index (cover the file in work tree by index)

  • git add . add all updates from work tree to index
  • git reset remove all updates from index
  • git checkout give up all updates that not be added to index (cover all updates in work tree by index)

Imperfect ultimate weapon add --interactive

   There is another more fashion option --interactive on command git add, -i in short. By the name, this is a interactive interface too. It seems like this:

           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:    unchanged        +1/-1 index.html

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> 

It’s shows status above which like git status but more clear, and commands below. There are 8 commands shown, and a ? hidden:

1 status         show the main interface, usually for refresh
2 update         add files into index, just like `add file` but not work for new files
3 revert         remove files from index,just like `reset file`
4 add untracked  add new files into index
5 patch          like `add -p file`
6 diff           show what in the index
7 quit           quit
8 help           help, about commands
?                help, about the interface

You can just use the index or the first letter instead of full command.

   There are 5 core commands: u r a p d. Each of them leads to a second level interface, the command prompt change from > to >>. In this level, we can choose files with index or unique prefix of filename. Currently, it’s not support segmental matching, fuzzy matching or *, ? yet. Nor tab hints and input history. Wish they will be added in the future. For now, the index is enough. We can add a - before the index or file path, to remove adding, Type Enter to confirm the selection and type Enter again to take the action.

   The add --i bring us a great interface, we can play fluently, without repeating git status. On the mode, this is the ultimate weapon. But it’s an imperfect weapon. There are add file, reset file and add -p file within the add -i. But reset -p file, checkout file, checkout -p file are missing. So, for right now the best way is still the 3Ps Sorcery.