git 2.7 (Q4 2015) will introduce branch sorting using directly git branch
:
See commit aa3bc55, commit aedcb7d, commit 1511b22, commit f65f139, ... (23 Sep 2015), commit aedcb7d, commit 1511b22, commit ca41799 (24 Sep 2015), and commit f65f139, ... (23 Sep 2015) by Karthik Nayak (KarthikNayak
).
(Merged by Junio C Hamano -- gitster
-- in commit 7f11b48, 15 Oct 2015)
In particular, commit aedcb7d:
branch.c
: use 'ref-filter
' APIs
Make 'branch.c
' use 'ref-filter
' APIs for iterating through refs sorting. This removes most of the code used in 'branch.c
' replacing it
with calls to the 'ref-filter
' library.
It adds the option --sort=<key>
:
Sort based on the key given.
Prefix -
to sort in descending order of the value.
You may use the --sort=<key>
option multiple times, in which case the last key becomes the primary key.
The keys supported are the same as those in git for-each-ref
.
Sort order defaults to sorting based on the full refname (including refs/...
prefix). This lists detached HEAD (if present) first, then local branches and finally remote-tracking branches.
Here:
git branch --sort=-committerdate
Or (see below with Git 2.19)
# if you are sure to /always/ want to see branches ordered by commits:
git config --global branch.sort -committerdate
git branch
See also commit 9e46833 (30 Oct 2015) by Karthik Nayak (KarthikNayak
).
Helped-by: Junio C Hamano (gitster
).
(Merged by Junio C Hamano -- gitster
-- in commit 415095f, 03 Nov 2015)
When sorting as per numerical values (e.g. --sort=objectsize
) there is no fallback comparison when both refs hold the same value. This can cause unexpected results (i.e. the order of listing refs with equal values cannot be pre-determined) as pointed out by Johannes Sixt ($gmane/280117).
Hence, fallback to alphabetical comparison based on the refname
whenever the other criterion is equal.
$ git branch --sort=objectsize
* (HEAD detached from fromtag)
branch-two
branch-one
master
With Git 2.19, the sort order can be set by default.
git branch
supports a config branch.sort
, like git tag
, which already had a config tag.sort
.
See commit 560ae1c (16 Aug 2018) by Samuel Maftoul (``).
(Merged by Junio C Hamano -- gitster
-- in commit d89db6f, 27 Aug 2018)
branch.sort:
This variable controls the sort ordering of branches when displayed by git-branch
.
Without the "--sort=<value>
" option provided, the value of this variable will be used as the default.
To list remote branches, use git branch -r --sort=objectsize
. The -r
flag causes it to list remote branches instead of local branches.
With Git 2.27 (Q2 2020), "git branch
" and other "for-each-ref
" variants accepted multiple --sort=<key>
options in the increasing order of precedence, but it had a few breakages around "--ignore-case
" handling, and tie-breaking with the refname, which have been fixed.
See commit 7c5045f, commit 76f9e56 (03 May 2020) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit 6de1630, 08 May 2020)
ref-filter
: apply fallback refname sort only after all user sorts
Signed-off-by: Jeff King
Commit 9e468334b4 ("ref-filter
: fallback on alphabetical comparison", 2015-10-30, Git v2.7.0-rc0 -- merge listed in batch #10) taught ref-filter's sort to fallback to comparing refnames.
But it did it at the wrong level, overriding the comparison result for a single "--sort
" key from the user, rather than after all sort keys have been exhausted.
This worked correctly for a single "--sort
" option, but not for multiple ones.
We'd break any ties in the first key with the refname and never evaluate the second key at all.
To make matters even more interesting, we only applied this fallback sometimes!
For a field like "taggeremail
" which requires a string comparison, we'd truly return the result of strcmp()
, even if it was 0.
But for numerical "value
" fields like "taggerdate
", we did apply the fallback. And that's why our multiple-sort test missed this: it uses taggeremail
as the main comparison.
So let's start by adding a much more rigorous test. We'll have a set of commits expressing every combination of two tagger emails, dates, and refnames. Then we can confirm that our sort is applied with the correct precedence, and we'll be hitting both the string and value comparators.
That does show the bug, and the fix is simple: moving the fallback to the outer compare_refs()
function, after all ref_sorting
keys have been exhausted.
Note that in the outer function we don't have an "ignore_case"
flag, as it's part of each individual ref_sorting
element. It's debatable what such a fallback should do, since we didn't use the user's keys to match.
But until now we have been trying to respect that flag, so the least-invasive thing is to try to continue to do so.
Since all callers in the current code either set the flag for all keys or for none, we can just pull the flag from the first key. In a hypothetical world where the user really can flip the case-insensitivity of keys separately, we may want to extend the code to distinguish that case from a blanket "--ignore-case
".
The implementation of "git branch --sort
"(man) wrt the detached HEAD display has always been hacky, which has been cleaned up with Git 2.31 (Q1 2021).
See commit 4045f65, commit 2708ce6, commit 7c269a7, commit d094748, commit 75c50e5 (07 Jan 2021), and commit 08bf6a8, commit ffdd02a (06 Jan 2021) by Ævar Arnfjörð Bjarmason (avar
).
(Merged by Junio C Hamano -- gitster
-- in commit 9e409d7, 25 Jan 2021)
branch
: show "HEAD detached" first under reverse sort
Signed-off-by: Ævar Arnfjörð Bjarmason
Change the output of the likes of "git branch -l --sort=-objectsize
"(man) to show the "(HEAD detached at <hash>)
" message at the start of the output.
Before the compare_detached_head()
function added in a preceding commit we'd emit this output as an emergent effect.
It doesn't make any sense to consider the objectsize, type or other non-attribute of the "(HEAD detached at <hash>)
" message for the purposes of sorting.
Let's always emit it at the top instead.
The only reason it was sorted in the first place is because we're injecting it into the ref-filter machinery so builtin/branch.c
doesn't need to do its own "am I detached?" detection.
git for-each-ref
from Jakub Narębski: you can get remote branches passingrefs/remotes/
instead ofrefs/heads/
(or you can pass both, whitespace-separated);refs/tags/
for tags, or justrefs/
for all three kinds. – jakub.gfor-each-ref
! You will use directlygit branch --sort=-committerdate
: see my answer below – VonC