GIT - The 'b*' Git commands


git bisect

Usage :

Use binary search to find the commit that introduced a bug

Imagine you wrote a program : it's made of several files, plenty of commits, and one of them introduced a bug (or anything unwanted). git bisect can help finding which commit caused that To do so, you'll have to tell Git whether commits are bad (the bug is present in this commit) or good (bug not present in this commit).

Manual execution

  1. get commit IDs :
  2. cd to the root of the repository :
  3. enter bisect mode :
    git bisect start
    you can also narrow the search area to some files / directories with :
    git bisect start -- someFile someDir
  4. specify a bad commit :
    • git bisect bad badCommitId
    • git bisect bad will generally suffice since the latest commit in history actually has the bug
  5. specify a good commit :
    git bisect good goodCommitId
  6. at that time, git bisect :
    1. picks a commit between those two endpoints
    2. performs a checkout on this specific commit (leaving the working directory in detached head)
    3. and expects you to specify whether the selected commit is good or bad. To figure this out, you'll have to execute your code, run unit tests, ..., then let Git know :
      • git bisect good
      • git bisect bad
      • git bisect skip (undetermined)
  7. git bisect continues narrowing down the range until it finds the exact commit that introduced the change and displays results
  8. at any point, leave bisect mode :
    git bisect reset

Automated execution


git blame

Usage :

Show what revision and author last modified each line of a file

About git annotate :

You may also read about the git annotate command and its differences with git blame.

git annotate is now obsolete, use git blame instead.


git branch

Usage :

List, create, or delete branches
For Git, a branch is just a label moving with a series of commits. Deleting a branch is then just removing this label, and does not actually delete commits. Without this label :
  • merged commits are now referenced by the branch they've been merged into, so everything is going extremely well
  • unmerged commits become "anonymous" (i.e. they belong to 0 branch). They'll be lost (nothing points to them) and "garbage collected" (i.e. deleted) shortly.

Flags :

Flag Usage
(none) list local branches. An asterisk * highlights the current branch.
-a list all branches : local + remote
git branch -a = git branch + git branch -r
-D branch Delete branch branch irrespective of its merged status
-d branch delete the fully merged branch branch
To delete a remote branch : git push -d remote branch
-f --force
git branch -f branch startPoint
forces Git to reset branch to startPoint, even if branch exists already. Otherwise, Without -f, git branch refuses to change an existing branch.
-m oldBranch newBranch Rename (move) oldBranch into newBranch :
git branch -m oldBranch newBranch
-r --remotes list the remote-tracking branches
-u remoteBranch
Set up localBranch's tracking information so remoteBranch is considered localBranch's upstream branch. If no localBranch is specified, then it defaults to the current branch.
git branch -u origin/branch
--unset-upstream branch Remove the upstream information for branch.
Defaults to the current branch if no branch is specified.
-v -vv --verbose when in list mode (toggled by -a and others), list not only the branch name (the default), but also :
  • the commit ID
  • the commit subject line (1st line of commit message)
  • relationship with upstream branch (if any)
  • -vv only : name of the upstream branch

Example :

Create a new empty branch :

basic command :

git branch myNewBranch

This new branch is not selected by default. To do so : git checkout myNewBranch

Create + checkout :

git checkout -b myNewBranch

Create a new branch from a known point :

Create a new branch called branch. If not provided, startPoint defaults to HEAD :
git branch branch [startPoint]

Let's give this a try

  1. Let's create a new repository and some commits :
    tmpDir=$(mktemp -d --tmpdir=/dev/shm tmpDir.XXXXXXXX); cd "$tmpDir"; git init; echo 'hello world' > myFile; git add myFile; git co -m 'initial version'; for i in {A..D}; do echo $i >> myFile; git add myFile; git co -m "Added '$i'"; done; git log --graph --oneline --decorate
    * 1d791e1 (HEAD ->  master) Added 'D'
    * 7d07f0e Added 'C'
    * b5e0a67 Added 'B'	this will become the "common ancestor" to both branches
    * 7177ae6 Added 'A'
    * b728cee initial version
  2. Now we create a new branch then "jump" on it :
    git branch myNewBranch b5e0a67; git checkout myNewBranch
    Switched to branch 'myNewBranch'
  3. Creating some commits in the new branch :
    for i in {E..H}; do echo $i >> myFile; git add myFile; git co -m "Added '$i'"; done; git log --graph --oneline --decorate
    * bad4a24 (HEAD ->  myNewBranch) Added 'H'
    * 680aaa3 Added 'G'
    * 52ded27 Added 'F'
    * 5cf765a Added 'E'
    * b5e0a67 Added 'B'	actually the ancestor of the 2nd round of commits (as expected)
    * 7177ae6 Added 'A'
    * b728cee initial version
  4. Back to our first branch, what can we see ?
    git checkout master; git log --graph --oneline --decorate
    Switched to branch 'master'
    * 1d791e1 (HEAD ->  master) Added 'D'
    * 7d07f0e Added 'C'
    * b5e0a67 Added 'B'	still there, with the same children
    * 7177ae6 Added 'A'
    * b728cee initial version
  5. Done playing, let's clean our mess before leaving :
    cd ..; [ -d "$tmpDir" ] && rm -rf "$tmpDir"

Get the current Git branch in a shell script :

currentGitBranch=$(git branch 2>/dev/null | awk '/^\*/ {print $2}')
echo "$currentGitBranch"