A Config Option for the Initial Branch Name
Since the earliest days of Git, git init has created the first branch in a new repository with the hard-coded name master. Git 2.28 introduces a configuration option, init.defaultBranch, that replaces that hard-coded value.
When you run git init in a fresh repository now, Git checks the value of init.defaultBranch to decide what to call the initial branch. If the option isn't set, the default remains master. Setting the option is a one-line affair:
$ git config --global init.defaultBranch main
Two important caveats accompany this change. First, init.defaultBranch only affects new repositories created after you set it — existing projects are never renamed by this option. Second, git clone continues to follow the HEAD of the remote repository, so the branch you check out after cloning is always whatever the remote maintainer has designated as the default, regardless of your local configuration.
This adjustment is part of a broader community effort. The Software Freedom Conservancy has published a statement on the topic, and GitHub, GitLab, and Bitbucket have all begun exploring renaming their own defaults away from master.
Faster Path History with Bloom Filters
Git 2.27 extended the commit-graph file format to include changed-path Bloom filters. Git 2.28 now leverages that data for concrete performance wins in commands that trace file history.
To understand the benefit, it helps to recall what the commit-graph file does. Git stores objects either as loose files on disk or compressed inside *.pack files. Either way, reading a commit's metadata — its parent hashes and root tree — normally requires parsing and decompressing the object. The commit-graph file acts as a cache: for any commit listed in it, Git reads those fields directly from the graph file, skipping decompression entirely. The file also stores computed values, such as generation numbers, which speed up reachability and topological ordering questions.
Bloom filters add another computed layer. A Bloom filter is a probabilistic set: querying it for an item yields either "definitely not present" or "possibly present." For each commit in the commit-graph, Git now stores a Bloom filter populated with the paths changed between that commit and its first parent. When a command needs to know whether a commit touched a particular path, it can consult the filter and, on a "definitely not" answer, skip that commit without computing a diff.
The effect shows up most clearly in commands such as git log -- <path>, git log -L, and git blame. Before the filters existed, git log -- <path> had to compute a diff for every commit in its walk to decide whether the commit changed the path in question. With Bloom filters in place, Git skips diff computation entirely for commits whose filter rules out the path. Since diffs are relative expensive, avoiding them across a long history can produce substantial speedups. The technical details are covered in depth elsewhere, but enabling the feature locally is simple:
$ git commit-graph write --reachable --changed-paths
That command writes a commit-graph file with changed-path Bloom filters enabled, after which path-sensitive history commands should start showing improvements.
Tracing Merged History
git log -- <path> is the standard way to find which commits modified a file, but it has a blind spot: merge commits that brought those changes onto the mainline are typically skipped, since the merges themselves don’t alter the path. For anyone trying to reconstruct how a change actually landed, that omission can be frustrating.
Git 2.28 adds a --show-pulls flag to revision-walking commands such as git log and git rev-list. This flag brings those merge commits back into the output, making it easier to see not just the individual commits but also the points at which they were integrated. A particularly useful invocation is:
$ git log --oneline --graph --show-pulls -- <path>
Honest Pulls and Better Reporting
Running git pull while tracking a remote branch usually lands in one of four states: no changes anywhere, changes only on the server, changes only locally, or changes on both sides. The first three are straightforward—nothing to do, fast-forward, or just push. But when there is divergence on both ends, Git’s behavior depends entirely on whether the pull.rebase configuration is set.
If it is set, the local branch is rebased onto the upstream state. If not, Git performs a merge, which can clutter history and make it awkward to redo the pull cleanly. Git 2.28 now warns about this scenario—specifically when pull.rebase is unset and neither --rebase nor --no-rebase was passed explicitly—so you know what you’re getting into before the merge commit appears.
Contributing and Reporting via GitHub
Git’s development remains mailing-list-centric, but Git 2.28 lowers the barrier for GitHub-oriented contributors. The project now includes a GitHub Actions workflow in its repository that runs Git’s integration test suite across multiple platforms and compilers. Anyone with a fork of git/git gets these tests automatically on every push, no extra configuration required.
For those who would rather open a pull request than compose an email, the GitHub workflow also works with GitGitGadget. That service can submit your pull request to the mailing list on your behalf, letting you contribute to Git end-to-end without ever touching an email client.
For the reverse direction—reporting bugs—a new git bugreport command opens your $EDITOR with a pre-filled template of debugging questions, along with useful system details like your CPU architecture and Git version. The finished file can be sent directly to the Git mailing list as a complete, helpful bug report.
Filter Protocol and Sparse Checkout Clarity
The long-running process filter protocol, which sits on top of Git’s clean and smudge filters, got more informative in Git 2.27. In addition to content, the protocol now passes metadata such as the branch being checked out during git checkout or the remote involved in a git fetch. Tools like Git LFS can use this to determine which remote should be contacted for additional data, instead of relying on assumptions.
Finally, git status has a new awareness of sparse checkouts. When you are working in a sparse checkout, the command now reports what percentage of files are actually checked out. And for users of git-prompt.sh, the shell prompt will display SPARSE in such repositories, serving as a reminder that not all files are present locally.
Further Reading
This covers only a selection of the changes introduced in Git 2.27 and 2.28. The complete lists are available in the release notes for 2.27 and 2.28.



