Six cool features of the Git 2.x series

October 26th 2015 Nicola Paolucci in Git

It's been a while since I have reviewed Git release notes but that does not mean I haven't been eagerly reading more recent ones and incorporating new gems in my routines. To celebrate my birthday (yay!) and the recent release of Bitbucket Server I have collected a round of my favorite features for the Git 2.x series (up to 2.6). Let me know if you end up using any of these.

Stashing your changes before rebase

In Git 2.6 the rebase command received welcome attention. One of the more interesting new flags is this one:

git rebase --autostash

You can now specify if uncommitted changes should be stashed temporarily when you start a rebase or if the operation should fail in that case. You can make this behavior permanent by setting a new configuration option rebase.autostash.

If you set it to true Git will stash your uncommitted changes before the start of the rebase and it will re-apply the changes at the end of the operation. If you turn on this feature be ready to handle possible conflicts when the stash content is popped back.

If you turned on the autostash flag you will be able to temporarily disable it with the new --no-autostash command line option, courtesy of upcoming release 2.7.

Better handling of multiple clones: Git Worktree

A new worktree command has been introduced in Git 2.5. It allows you to maintain multiple checked out work trees of the same root repository. It's fantastic.

Before this command existed one had a few ways to switch context and work on different streams at the same time:

  • Stash the current uncommitted changes and checkout a different branch (using git stash).
  • Create work-in-progress (i.e. WIP) commits in the current branch to save ones work and switch branch.
  • Create a separate full clone of the repository to keep two ongoing development efforts separate and parallel.
  • Set the GIT_WORK_TREE or GIT_DIR environment variables, as explained here.

With worktree one can create a sub-folder of the root project with a specific checked out branch - or ref for that matter - and keep working on it until done. The command saves you from having to create separate - out of band - clones.

So imagine you're happily working on the develop branch but you are tasked to work for a few days on the rewrite branch that has massive changes. You can create a worktree for this extended work and leave your other ongoing effort where it is. The syntax of the command is:

git worktree add [-f] [--detach] [-b <new-branch>] <path> [<branch>]

So in our example the command would become:

git worktree add rewrite-folder rewrite

The command creates a sub-folder in the root of your project named - surprise - rewrite-folder which contains the checked out branch rewrite.

We can push and pull from that sub-folder as if we are at the root of our Git project and when we are done with that work we can simply delete the folder rewrite-folder. The administrative files stored in .git/worktrees will be eventually pruned, but if you want to be thorough you can easily prune that data with:

git worktree prune [-n] [-v] [--expire <expire>]

Quicker fixes with git commit --fixup

This tip came to my attention because Git contributor Luke Diamand, interviewed in the most recent Git Rev News, mentioned that his favorite Git feature nowadays is git commit --fixup.

Here's the setting to understand how to use it. You are working on a feature or a bug fix; You haven't shared that code with anyone else yet and you realise that one of the previous non-HEAD commits should be changed - maybe to remove code which should not be there, maybe to fix a bug without creating an extra - unclean - commit. Here's where commit --fixup comes in:

Say I have a commit with id 026b6b5 that reads:

026b6b5 [2 days ago] Implement feature A [Author]

I make some changes to my work tree and I just mark this as a "fixup" of that feature commit:

git commit -a --fixup 026b6b5

This command will create a commit for you that looks like this:

4b7076f [6 seconds ago] (HEAD -> feature-a) fixup! Implement feature A [Author]

The final step before I push this branch is to interactively rebase it using --autosquash. Git will pick up all the annotated commits, like the fixup! one above and weave the fix into the right place:

git rebase --interactive --autosquash master

The command above will open an editor on the rebase interactive sheet with the correct action already pre-filled for you. You can omit the --autosquash option if you have set the global configuration variable rebase.autosquash=true in your .gitconfig.

Track topic branches with show-branch --topics

The command show-branch - in the words of the Git documentation - "shows the commit ancestry graph starting from the commits named with <rev>s or <globs>s semi-visually".

Adding the --topics flag will show only commits that are NOT on the branch given. This is useful to quickly hide commits that already appear in the main branch. A refinement has been added on 2.5 that makes the command:

git show-branch --topics HEAD

Compare the given commit against all the local branches. For example in the project containing this blog the result is:

git show-branch --topics HEAD

! [HEAD] Final Draft
 * [blog/six-great-features-of-git-2.x] Final Draft
  ! [blog/git-power-routines] Added slideshare embed
   ! [blog/move-to-vertx-be-reactive] Initial markdown conversion, missing images
    ! [blog/reduce-build-time-with-java-8] Code cleanup
     ! [develop] Changing API to APIs category: we already have some of those.
      ! [master] minor tweaks, spelling, etc
       ! [pull-request-viewer-for-bitbucket-with-react-native] Merged in blog/osx-static-golang-binaries (pull request #159)
--------
   +     [blog/move-to-vertx-be-reactive] Initial markdown conversion, missing images
    +    [blog/reduce-build-time-with-java-8] Code cleanup
    +    [blog/reduce-build-time-with-java-8^] Initial port from Confluence 
+*++++++ [blog/git-power-routines] Added slideshare embed

Negative "grepping"

Sometimes you want to look for commits that do not have a specific string in their messages. This is where the new flag --invert-grep comes in. For example find all commits that do not contain a certain issue id:

git log --invert-grep --grep=PRJ-25

It will return all commits that do not contain PRJ-25 in the commit messages. Nifty.

Atomic pushes

You can define the default behavior of a naked git push by setting the push.default configuration parameter. In the cases where the push command is updating more than one ref - like when pushing multiple branches or tags at the same time - you can now specify a new option --atomic that guarantees the updates either all succeed or all fail.

Conclusions

There you have it. The drop is done. Did you find anything interesting? Are you using other esoteric flags in your daily development work? I'd like to hear about it, I am always eager to learn new tricks and flags so tweet at me at @durdn or @atlassiandev or leave a comment here.