Introduction

We use git for version control. In this article we'll go through our current workflow, present you with some tips on things you can do to keep your co-workers happy, and describe a couple of our favorite commands in order to encourage you to use the tool more.

Configuration

Make sure that your ~/.gitconfig is properly configured. It should, at a minimum, contain the following:

[user]
    name = <your name>
    email = <username@helicon.ai>

[init]
  defaultBranch = main

Use feature branches

We rely mostly on Feature branching during day-to-day operations. Encapsulating features in branches works well with our other practices and makes it easy to work on features in parallel without disturbing the main codebase.

Whenever a developer (or pair) starts working on a new feature they create branch off of the latest version of main. The branch gets a descriptive name in the form of feature/customer-support-form.

Other prefixes can be used to add context about certain types of branches. Bugs should be fixed in branches following the pattern fix/session-timeout-issue and experimental branches can be prefixed like experiment/c6m-transfunctioner-v2.

Make your changes, commit, test, and push as if you were working on main.

When you're done, make sure your local main is up-to-date with upstream main and merge the updated version into your feature branch. Resolve any merge conflicts and run the tests again.

If you're working with code reviews you can now create a merge request. Fix any problems with locally in the feature branch to ensure that they are visible in the MR.

If you're not working with code reviews you're free to merge the code into main at this point.

Be sure to delete the feature branch when the dust has settled. Especially if you're using squash merging as there's no reference to the feature branch in the history!

General things to consider

  • Make incremental, small changes to prevent unnecessary conflicts and to ensure that the code is shared within the team frequently.
  • Keep commits reasonably atomic. Two bugs fixed? Two commits. A bunch of related changes? Might be nice to have in a single commit.
  • Write descriptive commit messages. Start with a short summary in imperative mood and follow up with a more detailed description in the body of the message. The body is optional, but it is an oftentimes useful medium to convey implementation details or reasons for doing things a certain way.

Use the tool more

The versatility of git is impressive. The documentation is immense and -- while really well-written -- can be a bit overwhelming. This section contains a couple of useful features that you may want to add to your daily workflow.

Modify last commit

You can use --amend to add staged changes to the previous commit (adding a file you forgot to stage in a commit for example) or to change the last commit message if there are no local changes:

git commit --amend

Quickly switch to the previous branch

You can switch to the previously checked out branch with:

git checkout -

Bisect

Sometimes you're uncertain when something problematic was introduced into the codebase. Normally you'd go back in time to find a commit that does not have the problem and start going forward until you find the commit that introduced it. This linear approach can be very time consuming in larger projects.

git bisect speeds this process up by performing an interactive binary search of the history.

Start the process:

git bisect start

You're now in bisect mode! You need to find a good and a bad commit to get started. Poke around using git log and git checkout to find one of each and tell bisect about it to continue:

git bisect good <hash of good commit>
git bisect bad <hash of bad commit>

The interactive mode will guide you through the rest of the process. You'll be presented with different commits. Verify whether the problem exists in the commit or not and mark it as good or bad using the commands mentioned above. If the problem does exist in the commit bisect checked out you can run:

git bisect bad

If it wasn't present, let bisect know with:

git bisect good

After a series of checkouts you'll eventually end up with the commit that introduced the problem.

When you're done, or if you at any time want to stop bisecting, simply run:

git bisect reset

Interactively select parts of tracked files

You can interactively select which parts of tracked files you want to add to a commit with the following:

git add -p

That will start an interactive session in which you can choose to stage a "hunk" (diff terminology, essentially means a change), not to stage a hunk, split it etc. You can use ? during the interactive session to get a description of the different commands that are available.

Undoing commits

It is often best to use git revert when undoing commits. That way we're not touching the commit history more than necessary.

Locate the commit you want to revert using git log. Then revert the
commit:

git revert <hash>

Add any relevant information to the commit message and you're done.

An alternative, if you just want to undo local commits, is to use git reset. To undo the last commit you can use:

git reset HEAD~1

Branch overview

When you want an overview of the history and its branches you can use:

git log --all --decorate --graph

Or for an even terser version:

git log --all --decorate --graph --oneline

See what changed since when

Another awesome command is git whatchanged. You can use it to see what has changed since a specific moment in time:

git whatchanged --since="5 minutes"

Search the log

You can use the -S flag when running git log to search the log for code matching the query. Use the -p flag to display the full diff for each commit.

git log -p -S <query>

Find the culprit :)

git blame is a very useful command when you need some context to understand why a section of a file was modified.

Example:

git blame -L 2,3 README.md

The above will show you who was the last to modify lines 2 - 3.

Want to know more about how we can work together and launch a successful digital energy service?