A quick two months after 2.6, Git 2.7 has been released with a plethora of features, fixes and performance improvements. Here’s some of the new stuff we found interesting on the Bitbucket team.
Rounding out git worktree
The awesome git worktree
command, introduced in Git 2.5, lets you check out and work on multiple repository branches in separate directories, simultaneously. For example, if you need to make a quick hotfix and don’t want to mess with your working copy you can check out a new branch in a new directory with:
$ git worktree add -b hotfix/BB-1234 ../hotfix/BB-1234
Preparing ../hotfix/BB-1234 (identifier BB-1234)
HEAD is now at 886e0ba Merged in bedwards/BB-13430-api-merge-pr (pull request #7822)
Git 2.7 adds the git worktree list
subcommand to display your repository’s worktrees (and their associated branches):
$ git worktree list
/Users/kannonboy/src/bitbucket/bitbucket 37732bd [master]
/Users/kannonboy/src/bitbucket/staging d5924bc [staging]
/Users/kannonboy/src/bitbucket/hotfix/BB-1234 37732bd [hotfix/BB-1234]
The git bisect
command’s support for worktrees has also been improved. The refs that bisect
uses to track “good” and “bad” commits have been moved from .git/refs/bisect
to .git/refs/worktrees/$worktree_name/refs/bisect
so you can run bisects concurrently across multiple worktrees.
For completeness, as of Git 2.7, you can also now clone a worktree on disk. This creates a new, independent Git repository (not another worktree).
Fun-fact: Git worktrees aren’t just for branches! When researching the new Git functionality for this post I compared the Git v2.6.0 and v2.7.0 tags in parallel by checking them out in separate worktrees and building them:
$ git worktree add ../git-v2.6.0 v2.6.0
Preparing ../git-v2.6.0 (identifier git-v2.6.0)
HEAD is now at be08dee Git 2.6
$ git worktree add ../git-v2.7.0 v2.7.0
Preparing ../git-v2.7.0 (identifier git-v2.7.0)
HEAD is now at 7548842 Git 2.7
$ git worktree list
/Users/kannonboy/src/git 7548842 [master]
/Users/kannonboy/src/git-v2.6.0 be08dee (detached HEAD)
/Users/kannonboy/src/git-v2.7.0 7548842 (detached HEAD)
$ cd ../git-v2.7.0 && make
A couple of git stash
improvements
If you’re a fan of git rebase
, you might be familiar with the --autostash
option. It automatically stashes any local changes made to your working copy before rebasing, and reapplies them after the rebase is completed.
$ git rebase master --autostash
Created autostash: 54f212a
HEAD is now at 8303dca It's a kludge, but put the tuple from the database in the cache.
First, rewinding head to replay your work on top of it..
Applied autostash.
This is handy as it allows you to rebase from a dirty worktree. There’s also a handy config flag named rebase.autostash
to make this behaviour the default, which you can enable globally with:
$ git config --global rebase.autostash true
rebase.autostash
has actually been available since Git 1.8.4, but 2.7 introduces the ability to cancel this flag with the --no-autostash
option. I think this is mainly for completeness, as using it only seems to give you the dirty worktree warning:
$ git rebase master --no-autostash
Cannot rebase: You have unstaged changes.
Please commit or stash them.
I might be missing something though, let me know if you have a proper use case!
Speaking of config flags, Git 2.7 also introduces stash.showPatch
. The default behaviour of git stash show
is to display a summary of your stashed files.
$ git stash show
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Passing the -p
flag puts git stash show
into “patch mode”, which displays the full diff:
diff --git a/package.json b/package.json
index c876b26..e21eeb3 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
"mkdirp": "^0.5.0",
"byline": "^4.2.1",
"express": "~3.3.4",
- "git-guilt": "^0.1.0",
+ "git-guilt": "^0.1.1",
"jsonfile": "^2.0.0",
"jugglingdb-sqlite3": "0.0.5",
"jugglingdb-postgres": "~0.1.0",
stash.showPatch
makes this behaviour the default. You can enable it with:
$ git config --global stash.showPatch true
If you enable stash.showPatch
but then decide you want to view just the file summary, you can get the old behaviour back by passing the --stat
option instead.
$ git stash show --stat
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
As an aside: --no-patch
is a valid option, but doesn’t negate stash.showPatch
as you’d expect.
git filter-branch
gained speed (and a progress bar)
git filter-branch
is a versatile tool for rewriting git history. Since every commit object has a reference to its parents (and transitively to all of its ancestors), rewriting a particular commit means rewriting all of its successors as well. This means that even trivial history rewriting operations can take some time if you pick an older commit.
Git 2.7 introduces a nifty new progress bar that estimates how much time a filter-branch
command will take to complete:
As a bonus, filter-branch
commands that don’t modify the index or tree, now skip reading the index entirely, leading to dramatically better performance. The commit-filter
command in the GIF above simply rewrites the Git author associated with each commit name from my real name (“Tim Pettersen”), to my handle (“Kannonboy”), and doesn’t touch the tree. It took only ~38s to rewrite the first 1000 commits of Bitbucket Server under Git 2.7.0, versus ~64s under Git 2.6.0: an impressive ~40% improvement. The tests introduced with the performance improvements show even more dramatic savings of ~60%.
Improved negation in .gitignore
.gitignore
files let you exclude certain files that reside within your worktree being staged in your repository. You can negate these patterns by prepending a !
to “unignore” a particular file. For example:
# .gitignore
*.json
!cat.json
Will ignore all json
files except cat.json
.
However, in Git 2.6, you couldn’t apply a negation to a file residing in a directory that had been ignored.
# .gitignore
/animals
!/animals/cat.json # <-- this was still ignored (pre Git 2.7)
As of Git 2.7, the second example above also works. You can now apply a !
to “unignore” files in directories that would otherwise be ignored.
But wait, there’s more!
These are just a small sample of the Git goodness that landed in 2.7. For the full scoop, check out Junio C Hamano’s release notes, or peruse the Git repository yourself with:
$ git log v2.6.0..v2.7.0
If you’ve enjoyed this post or have questions about Git or Bitbucket, drop me a line on Twitter: I’m @kannonboy.