However you may not know why Git’s stashes have such awkward identifiers — stash@{1} , stash@{2} , etc. — and may have written them off as “just one of those Git idiosyncrasies”. It turns out that like many Git features, these weird IDs are actually a symptom of a very clever use (or abuse) of the Git data model.

Under the hood, the git stash command actually creates a set of special commit objects that encode your stashed changes, and maintains a reflog that holds references to these special commits. This is why the output from git stash list looks a lot like the output from the git reflog command. When you run git stash apply stash@{1} , you're actually saying “apply the commit at position 1 from the stash reflog”.

As of Git 2.11, you no longer have to use the full git stash@{n} syntax. Instead, you can reference stashes with a simple integer indicating their position in the stash reflog:

$ git stash show 1

$ git stash apply 1

$ git stash pop 1

And so forth. If you’d like to learn more about how stashes are stored, I wrote a little bit about it in How git stash works.

Long running filter processes

Bitbucket supports the popular Git LFS (Large File Storage) extension to help users who need to efficiently track large binary files in their repositories. Git 2.11 comes with a couple of improvements to make Git LFS much faster and more pleasant to use. The most important change is that Git now supports long-running clean and smudge filter processes for transforming LFS pointers, rather than having to invoke a new process each time.

When you git add a file, clean filters can be used to transform (or clean) the file’s contents before being written to the Git object store. Git LFS reduces your repository size by using a clean filter to squirrel away large file content in the LFS cache, and adds a tiny “pointer” file to the Git object store instead.

The Git LFS clean filter converts large files into tiny pointer files.

Smudge filters are the opposite of clean filters — hence the name. When file content is read from the Git object store during a git checkout , smudge filters have a chance to transform it before it’s written to the user’s working copy. The Git LFS smudge filter transforms pointer files by replacing them with the corresponding large file, either from your LFS cache or by reading through to your Git LFS store on Bitbucket.

The Git LFS smudge filter converts pointer files back into the large file content.

Traditionally smudge and clean filter processes were invoked once per file that was being added or checked out. So a project with 1,000 files tracked with Git LFS invoked the git-lfs-smudge command 1,000 times for a fresh checkout! While each operation is relatively quick, the overhead of spinning up 1,000 individual smudge processes is costly.

As of Git 2.11, smudge and clean filters can be defined as long running processes that are invoked once for the first filtered file, then fed subsequent files that need smudging or cleaning until the parent Git operation exits. Lars Schneider, who contributed long running filters to Git, nicely summarized the impact of the change on Git LFS performance:

Filter process is 💥80x faster💥 on macOS and 💥 58x faster💥 on Windows for the test repo with 12k files. On Windows that means the tests runs in 57 seconds instead of 55 minutes!

That’s a seriously impressive performance gain! Note that you’ll need to upgrade to both Git 2.11 and Git LFS 1.5 to take advantage of these speed improvements.

git cat-file --filters