GIT - HowTo's about "staging"

mail

How can I instruct Git to ignore some files without altering the .gitignore file ?

Situation

Developers often create temporary / test files they don't want to commit. .gitignore is very convenient to avoid committing such files by mistake, but might become messy if the team has many members, each with their personal flavor of "test files" not worth committing .

Solution

Just use $GIT_DIR/info/exclude as a personal won't-be-committed .gitignore :

Once in the Git repository root dir : echo myTestFile >> .git/info/exclude

Alternate solution

Replace :
git status
with :
git status --untracked-files=no
(or create the corresponding alias).
Technically, this does not prevent "test files" from being committed : they are just hidden from the output of git status.
mail

How to detect moved (cut + paste) code ?

When text is moved (i.e. cut + paste) from one place to another, Git reports it as The --color-moved option (added in Git v2.15) highlights such text with different colors. It works with any command that generates a diff : This option has :

Usage :

One shot :

  1. git config --local color.diff.oldMoved magenta; git config --local color.diff.newMoved yellow
  2. git diff --color-moved=mode

Permanently :

  1. set the options above globally :
  2. or, in ~/.gitconfig :
    [diff]
    	
    	colorMoved = mode
  3. you can also define your own colors to highlight added / removed / moved contents (available colors + attributes) :
    [color "diff"]
    	new = bold green
    	old = bold red
    	newMoved = dim green
    	oldMoved = dim red
  4. then, as usual : git diff
    If you have an alias for git diff, it is possible one of its options (like --color-words) conflicts with --color-moved and makes some of your --color-moved settings inoperative.

Try it :

currentDir="$PWD"; tmpDir=$(mktemp -d); tmpFile='myFile'; cd "$tmpDir"; git init; echo -e "Let's go buy some apples.\n\tI want some.\nBanana is my favorite fruit.\nWho wants some coconut ?\n" > "$tmpFile"; echo -e '\nBEFORE :'; cat "$tmpFile"; git add "$tmpFile"; git commit -m 'initial version'; echo -e "Let's go buy some apples.\n\tI'll buy 4.\nWho wants some coconut ?\n\tYes, please.\nBanana is my favorite fruit.\n" > "$tmpFile"; echo -e '\nAFTER :'; cat "$tmpFile"; echo -e '\ngit diff --color-moved=no :'; git diff --color-moved=no; echo -e '\ngit diff --color-moved=blocks :'; git config --local color.diff.oldMoved magenta; git config --local color.diff.newMoved yellow; git diff --color-moved=blocks; cd "$currentDir"; [ -d "$tmpDir" ] && rm -rf "$tmpDir"
BEFORE :
Let's go buy some apples.
	I want some.
Banana is my favorite fruit.
Who wants some coconut ?



AFTER :
Let's go buy some apples.
	I'll buy 4.
Who wants some coconut ?
	Yes, please.
Banana is my favorite fruit.


git diff --color-moved=no :
diff --git i/myFile w/myFile
index ff718be..a8bb615 100644
--- i/myFile
+++ w/myFile
@@ -1,5 +1,6 @@
 Let's go buy some apples.
-	I want some.
-Banana is my favorite fruit.
+	I'll buy 4.
 Who wants some coconut ?
+	Yes, please.
+Banana is my favorite fruit.


git diff --color-moved=blocks :
diff --git i/myFile w/myFile
index ff718be..a8bb615 100644
--- i/myFile
+++ w/myFile
@@ -1,5 +1,6 @@
 Let's go buy some apples.
-	I want some.
-Banana is my favorite fruit.
+	I'll buy 4.
 Who wants some coconut ?
+	Yes, please.
+Banana is my favorite fruit.
mail

How to view staged changes ?

mail

How to unstage a file ?

To "unstage" a file means "remove it from the index (aka staging area)". To do so :

git reset HEAD path/to/file

mail

How to stage / unstage a patch ?

Solution 1 : interactive staging (source) :

  1. Enter Git interactive staging : git add -i (or git add --interactive)
  2. Type p for patch
  3. Type the number of the file you'd like to take a patch from, then (twice : ???)
  4. This will display the hunks of the current file and offer keys :
    • y (yes) : stage this hunk
    • n (no) : do not stage this hunk
    • a (all) : stage this and all the remaining hunks in the file
    • d (don't) : do not stage this hunk nor any of the remaining hunks in the file
    • ? (help)
  5. When all the desired changes are staged, leave with q
  6. If something went wrong while staging changes, you can unstage some changes
  7. Then commit the staged changes : git commit -m 'What a beautiful commit'

Solution 2 : git add in patch mode : -p / --patch (source 1, 2) :

git add --patch myFile, then for each hunk :

  • y (yes) : stage this hunk
  • n (no) : do not stage this hunk
  • a (all) : stage this and all the remaining hunks in the file
  • d (don't) : do not stage this hunk nor any of the remaining hunks in the file
  • s (split) : split the current hunk into smaller hunks
  • / : search for a hunk matching the given regex
  • ? (help)
  • (there are more )
This -p option is a shortcut to the -i above + patch.

It is possible to do the opposite; i.e. interactively select hunks to unstage with git reset -p.

BONUS : How to stage a patch while having contiguous changed lines (source) ?

Let's start by simulating the situation :

  1. Create the Git repo, add some code and commit : myDir=~/testDir; myFile='testFile'; mkdir "$myDir"; cd "$myDir"; git init; echo 'line 1' >> "$myFile"; git add "$myFile"; git commit -m 'Initial version'
  2. Improve the code : echo -e "line 2\nline 3" >> "$myFile"
  3. Our program now looks like : cat "$myFile"
    line 1
    line 2
    line 3
  4. And Git sees some difference : git diff
    diff --git a/testFile b/testFile
    index 89b24ec..a92d664 100644
    --- a/testFile
    +++ b/testFile
    @@ -1 +1,3 @@
     line 1
    +line 2
    +line 3

And the question is : how may I stage line 2 split from line 3 so that I can make 2 separate commits ?

  • With git add --patch, the split command is useless since the changed lines are contiguous
  • Still with git add --patch, the regex search function doesn't work so far (returns all lines, whatever pattern I enter)

Now the solution :

  1. Prepare to stage changes in patch mode : git add --patch
    diff --git a/testFile b/testFile
    index 89b24ec..a92d664 100644
    --- a/testFile
    +++ b/testFile
    @@ -1 +1,3 @@
     line 1
    +line 2
    +line 3
    Stage this hunk [y,n,q,a,d,/,e,?]?
  2. Then type e to edit the hunk. This will open your default text editor
  3. Once in the editor, follow the instructions to specify which lines to stage or not :
    # Manual hunk edit mode -- see bottom for a quick guide
    @@ -1 +1,3 @@
     line 1
    +line 2
    #+line 3
    # ---
    # To remove '-' lines, make them ' ' lines (context).
    # To remove '+' lines, delete them.
    # Lines starting with # will be removed.
    #
    # If the patch applies cleanly, the edited hunk will immediately be
    # marked for staging. If it does not apply cleanly, you will be given
    # an opportunity to edit again. If all lines of the hunk are removed,
    # then the edit is aborted and the hunk is left unchanged.
    To not stage a ... line Do :
    +
    • add a leading #
    • OR delete this line
    - change the leading - into a [SPACE]
    Save and exit.
    If you're using Emacs and this step fails saying things like patch fragment without header and Your edited hunk does not apply, read this.
  4. View staged changes : git diff --staged
    diff --git a/testFile b/testFile
    index 89b24ec..7bba8c8 100644
    --- a/testFile
    +++ b/testFile
    @@ -1 +1,2 @@
     line 1
    +line 2
  5. Commit. Enjoy.
mail

How to save a WIP ?

You may have to save a WIP (i.e. changes made after the previous commit are not ready for a new commit yet) before changing branch. To do so :
  1. git stash
  2. git status should return no modified file
  3. create a new branch "newBranch", work hard, commit
  4. come back to the original branch : git checkout previousBranch
  5. restore WIP in previousBranch : git stash apply
  6. OR drop "stashed" content : git stash drop
mail

How to discard local uncommitted changes ?

If you need to unstage changes first :
git reset
Undo all local uncommitted changes :
Undo local uncommitted changes for a specific file :
git checkout someFile
mail

How to create and apply a patch ?

Step 1 — create the patch from the repository having the code change

There are several methods to do so : Once the patch has been generated, you can inspect it with your favorite text editor and send it to whoever needs it.

Step 2 — apply the patch on its counterpart repository

TO be proof-check / experienced / confirmed :

applying the patch :
- "git am ..." : the example below looks weird. To be checked again
- "git apply <patch>" : tested and OK with a patch created with "git diff" :-)
  1. list changes provided by the patch : git apply --stat theNameOfMy.patch
  2. check / dry-run the patch : git apply --check theNameOfMy.patch
  3. if the previous command output no error, you can now apply the patch : git am --signoff < theNameOfMy.patch
  4. check the logs : git log will output
    commit 7d7a786c1353b5433b7e39a6b50c7ac8d9b5159f
    Author: Thomas ANDERSON <thomas.anderson@metacortex.com>	who made the commit
    Date:	Fri Jul 18 09:35:36 2014 +0200
    
    	FIX a glitch in the Matrix
    
    	Signed-off-by: Smith <smith@thematrix>		who applied the patch