Git 6: Branching

Photo by Nick Nice on Unsplash

Photo by Nick Nice on Unsplash

The Power Of Branching

Excerpts of this have been taken from the Git book.

Much has been written on the power of Git’s branching system. Indeed it has been called Git’s killer feature. A lot of version control systems tried and failed to get this point implemented. Git is one of the few, in my humble opinion, who gets it right.

As shown in Why Git?, Git doesn’t store data as a series of changesets or differences, but instead as a series of snapshots.

Commits are key to understanding branching so lets see 3 distinct commits:

commits-and-parents.png

Commits and their parents.

A branch means to open up an offshoot of new code, hoping to get something useful done. Code branching in three different directions is shown below:

commit-and-tree.png

A commit and its tree. Here this code has branched in 3 directions.

Branches work just like pointers in C/C++, as does HEAD. Below the v1.0, master, and HEAD are all pointing to commit f30ab. It in turn points to Snapshot C.

branch-and-history.png

A branch and its commit history.

Astute students of computer science will recognize the linked list data structure.

Now things start to get interesting. Let’s move forward in history a bit and work towards our branches diverging from one another:

git branch testing

This creates a new pointer to the same commit you’re currently on. Checkout the testing branch to point the HEAD it:

git checkout testing

head-to-testing.png

HEAD points to the current branch.

What is the significance of that? Well, let’s do another commit:

vim test.rb
git commit -a -m 'made a change'

Then checkout master and do some more changes:

git checkout master
vim test.rb
git commit -a -m 'made other changes'

branch-example.png

Divergent history.

Your project has now diverged. You can verify this with the git log command.

git log

This is the commit history one commit back for one of my repos:

commit 5a156325e12c0f4e820976d029d447369b88aee0 (HEAD -> master, gtri/master)
Author: Peter Barker <*email redacted*>
Date: Mon Aug 17 11:14:32 2020 +1000

mavproxy_tracker: fix for multi-vehicle handling changes

Push ‘q’ to exit.

Tagging

annie-spratt-AkKE1KOMD5E-unsplash-cropped.png

This is also covered very nicely in the Git book.

Like most VCSs, Git has the ability to tag specific points in a repository’s history as being important. Typically, people use this functionality to mark release points (v1.0v2.0 and so on).

Listing Your Tags

Listing the existing tags in Git is straightforward. Just type git tag (with optional -l or --list). This command lists the tags in alphabetical order; the order in which they are displayed has no real importance.

You can also search for tags that match a particular pattern. The Git source repo, for instance, contains more than 500 tags. If you’re interested only in looking at the 1.8.5 series, you can run this:

git tag -l "v1.8.5*"

Listing tag wildcards requires -l or --list option

If you want just the entire list of tags, running the command git tag implicitly assumes you want a listing and provides one; the use of -l or --list in this case is optional. If, however, you’re supplying a wildcard pattern to match tag names, the use of -l or --list is mandatory.

Creating Tags

Git supports two types of tags: lightweight and annotated.

A lightweight tag is very much like a branch that doesn’t change — it’s just a pointer to a specific commit.

Annotated tags, however, are stored as full objects in the Git database. They’re checksummed; contain the tagger name, email, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG). It’s generally recommended that you create annotated tags so you can have all this information; but if you want a temporary tag or for some reason don’t want to keep the other information, lightweight tags are available too.

Annotated Tags

Creating an annotated tag in Git is simple. The easiest way is to specify -a when you run the tag command:

git tag -a v1.4 -m "my version 1.4"

The -m specifies a tagging message, which is stored with the tag. If you don’t specify a message for an annotated tag, Git launches your editor so you can type it in.

You can see the tag data along with the commit that was tagged by using the git show command:

git show v1.4
tag v1.4
Tagger: Ben Straub <ben@straub.cc>
Date: Sat May 3 20:19:12 2014 -0700

my version 1.4

commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

Change version number

That shows the tagger information, the date the commit was tagged, and the annotation message before showing the commit information.

Lightweight Tags

Another way to tag commits is with a lightweight tag. This is basically the commit checksum stored in a file — no other information is kept. To create a lightweight tag, don’t supply any of the -a-s, or -m options, just provide a tag name:

git tag v1.4-lw
git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5

This time, if you run git show on the tag, you don’t see the extra tag information. The command just shows the commit:

git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

Change version number

Tagging Later

You can also tag commits after you’ve moved past them. Suppose your commit history looks like this:

git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' a6b4c97498bd301d84096da251c98a07c7723e65 Create write support 0d52aaab4479697da7686c15f77a3d64d9165190 One more thing 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment' 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc Add commit function 4682c3261057305bdd616e23b64b0857d832627b Add todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a Create write support 9fceb02d0ae598e95dc970b74767f19372d61af8 Update rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc Commit the todo 8a5cbc430f1a9c3d00faaeffd07798508422908a Update readme

Now, suppose you forgot to tag the project at v1.2, which was at the “Update rakefile” commit. You can add it after the fact. To tag that commit, you specify the commit checksum (or part of it) at the end of the command:

git tag -a v1.2 9fceb02

You can see that you’ve tagged the commit:

git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5

git show v1.2
tag v1.2
Tagger: Scott Chacon <schacon@gee-mail.com>
Date: Mon Feb 9 15:32:16 2009 -0800

version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon <mchacon@gee-mail.com>
Date: Sun Apr 27 20:43:35 2008 -0700

Update rakefile ...

3-Way vs 2-Way Merge

Because Git uses a simple three-way merge, merging from one branch into another multiple times over a long period is generally easy to do. This means you can have several branches that are always open and that you use for different stages of your development cycle; you can merge regularly from some of them into others. We’ll explore this further when we get into merging.

Creating a new branch and switching to it at the same time

It’s typical to create a new branch and want to switch to that new branch at the same time — this can be done in one operation with

git checkout -b <newbranchname>

From Git version 2.23 onwards you can use git switch instead of git checkout:

  • Switch to an existing branch: git switch testing-branch

  • Create a new branch and switch to it: git switch -c new-branch. The -c flag stands for create, you can also use the full flag: --create

  • Return to your previously checked out branch: git switch -

Next

Next time we’ll explore Git’s merging system.

Credits

https://linuxfromanoobie.wordpress.com/2018/05/05/git-branching-i/

Previous
Previous

Git 7: Merging

Next
Next

Git 5: Blobs and Trees