Crafting Commits

How should look the commits?

  • Atomic: self-contained, coherent (one commit, one logical change), relatively small
  • Consistent:
  • Incremental:
  • Documented:

Staging Commits

Add more changes to the same file

Following the example:

// Have 2 files modified
$ git status
README
Calculation.c

// Intention: create 2 commits
$ git add README
$ git status
stage: README
WD: Calculation.c

// See what is going in the next commit, that is in staged
$ git diff --staged

// See changes in Working Directory
$ git diff

// Do changes in README
$ vi README
// It is not included in stage area
$ git diff --staged
// It is in fact in WD
$ git diff

// What happen? Git  creates a cached copy of the modified file
$ git diff --cached

// It is needed to include it again to stage area
$ git add README

In order to remove the file from index (stage):

$ git reset HEAD
// Same as the following, update HEAD ref and stage to match the commit specified by the reference    
$ git reset --mixed

Doing an alias:

git config --global alias.unstage "reset HEAD"

Adding two changes from one file in two commits

Following the example:

// Use -p option for patch, to select hunks (parts to be included in one changed file)
$ git add -p calculation.c

// options:
// y: yes
// n: no
// j: jump to next unstaged hunk
// k: jump to previous unstaged hunk
// q: quit
// a:
// d: skip the rest of file
// /:
// s: split hunk into smaller ones
// e: edit, open the file in an editor to edit it manually
// ?:  

// See status, see the same file in staging and WD
$ git status

Interactive staging

Use it:

// git add -i

It displays all files and its stage and unstage area. Also the operations: update -> for stage all the file, patch -> for stage a hunk.

Verifying Commits

First think to check is Spaces.

// Using this command give us all the differences including the spaces
$ git diff
// If we want only spaces
$ git diff --check

How to check Git where to find for spaces

Using the global configuration:

// Using global config
$ git  config --global core-whitespace "blank-at-eol, blank-at-eof, tab-in-indent"

// Using tab-in-indent here tells Git to find for tabs instead of spaces

What are the options for check spaces in indentation

// Option: check with spaces that are used for indentation instead of tabs
indent-with-no-tab

// Option: check with tabs instead of spaces
tab-in-indent

How to remove whitespaces in only local commits

If there is a local commit, we can used:

git rebase --whitespace

// Search for diff between current and two commits before
$ git diff HEAD~2..HEAD
// To remove the spaces
$ git rebase HEAD~2 --whitespace=fix
// check that the spaces are removed, starting from the last 2 commits
$ git diff HEAD~2..HEAD

The power of stash

What is stash? Useful to test the stage area before commiting. It is a storage area where we can temporarily put unfinished work that we wish to take out of the working directory.

Stash area is conformed of modified files included in WD and Stage.

Example:

// Exist calculation.c which contains two functions: add and substract. I want to take out the substration
// function. This file is already in staged
$ git diff --staged
// Create the temporary area: stash which constains all files I dont want in my current commit
// --keep-index to keep the stage area as it is currently
// --include-untracked is to include all not yet tracked files in WD
$ git stash save --keep-index --include-untracked "WIP"
// See how are my changes, without the files stashed
$ git status

To see what is in stash:

$ git stash list

This is the time to run all our scripts to compile and test, to verify that the patch is going to leave the code in a consistent state.

// Example of compilation
$ make
// Proceed to commit
$ git commit -m "Supports addition"

Now it is time to restore the files in stash

// To restore the files without removing stash
$ git stash apply

// To restore the files and delete the stash
$ git stash pop

Documenting Commits

Documenting and validating messages:

  • Run a reminder, checking the well-formed commit message (automatically)
  • Manually doing Code Reviews to check the description

Wrap the description at 72 characters to make it readable on a standard 80-character console.

Well-formed commit message

There is a Git convention for commit messages.

  1. Create a little reminder every time someone is about to make a commit. This reminder could take the form of a shell script that runs every time a commit is created in the local repository. The script will check the commit message and notify user.
  2. In order to Git runs the script automatically: commit client-side hook
  3. Download the scripts in .git/hooks/
  4. wget http://git.io/validate-commit-msg --show-progress --quiet -O .git/hooks/commit-msg
    

The file looks like:

#!/bin/sh
#
# A hook script that checks the length of the commit message.
#
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.

DEFAULT="\033[0m"
YELLOW="\033[1;33m"

function printWarning {
    message=$1
    printf >&2 "${YELLOW}$message${DEFAULT}\n"
}

function printNewline {
    printf "\n"
}

function captureUserInput {
    # Assigns stdin to the keyboard
    exec < /dev/tty
}

function confirm {
    question=$1
    read -p "$question [y/n]"$'\n' -n 1 -r
}

messageFilePath=$1
message=$(cat $messageFilePath)
firstLine=$(printf "$message" | sed -n 1p)
firstLineLength=$(printf ${#firstLine})

test $firstLineLength -lt 51 || {
    printWarning "Tip: the first line of the commit message shouldn't be longer than 50 characters and yours was $firstLineLength."
    captureUserInput
    confirm "Do you want to modify the message in your editor or just commit it?"

    if [[ $REPLY =~ ^[Yy]$ ]]; then
        $EDITOR $messageFilePath
    fi

    printNewline
    exit 0
}

Leaving a Trail

It is important to have a clear trail of commits.

Interactive rebase

Useful to clearing up our local history before publishing. To rearrange the commit messages to be more clearly to the developer.

// Starting from the fourth commit ago
$ git rebase -i head~4
// Git opens a list of commits

Options allowed: p,r,e,s,f,x.

Example: allow reorder commits, pick, reword, edit, squash, etc.

To add new changes to the current stage:

$ vi calculation.c 
$ git add .
$  git commit --amend -C head

Continue with the interactive rebase:

$ git rebase --continue

See the comments history:

$ git lg master

Public vs Private History

Private = local repository

Public = remote repository

People can and propably should rebase their own work. That is cleanup. But never other people's code. That is a destroy history.

results matching ""

    No results matching ""