GIT - The 'b*' Git commands

mail

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

mail

git blame

Usage

Show which commit + author last modified each line of a file
  • You may have heard about git annotate. This command :
    • slightly differs from git blame on output formats only
    • exists for backward compatibility and provide a more familiar command name for non-Git users
  • If you're searching which commit deleted a line, consider git bisect

Flags

Flag Usage
--reverse commitId..commitId
  • walks history forward instead of backward
  • shows the last revision in which a line has existed
    • requires a range of revision like START..END where the path to blame exists in START
    • git blame --reverse START
      is short for
      git blame --reverse START..HEAD
mail

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 :
  • git branch
  • git branch newBranch
  • git branch newBranch startPoint
it depends :
  • list local branches. The current branch name will be displayed in green and highlighted with a leading asterisk : * myBranch.
  • create the newBranch branch (pointing to the current HEAD) but will not switch the working tree to it (i.e. does not perform a git checkout / git switch)
  • see details
-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 oldName newName Rename (move) branch oldName into newName :
git branch -m oldName newName
-r --remotes list the remote-tracking branches
-u remoteBranch
--set-upstream-to=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 newBranch

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

Create + checkout :

git checkout -b newBranch

Create a new branch from a known point :

Create a new branch called newBranch. If not provided, startPoint defaults to HEAD :
git branch newBranch [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 newBranch b5e0a67; git checkout newBranch
    Switched to branch 'newBranch'
  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 ->  newBranch) 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"

Get the base commit of a branch :

In other words, considering :
A---B---C---D---E	master
         \
          F---G---H	feature
how can I find C ?

Use git merge-base.