ogham/exa

Feature Request: sort files by git status

heafnerj opened this issue · 2 comments

Would it be possible to add a feature to sort files in a repo by their git status?

Oh that could be super useful!

I propose --sort git as the option flag.

Design Thoughts

Matching the order of git status output is probably the most intuitive.

git status prints file names with staged changes first, then file names with unstaged changes, and then finally untracked names (and within those groups, the sort is alphabetical/lexical).

exa has to adapt that a bit because it only has one entry per path (a file with both staged and unstanged changes only gets one entry, whereas in git status it gets listed twice) and because it might also be listing files that are in .gitignore. But I think the closer we get to an intuitive match between the two the better.

So I propose to sort by like this:

  1. paths with staged changes go before paths without staged changes,
  2. within that, paths with unstaged changes get sorted before paths without unstaged changes, and
  3. within that, new status N gets sorted before M (this last thing deviates from git status but I think it would make a big difference in certain situations, because then you reduce the frequency and unpredictability of where you need to notice N vs M distinctions in the listing... also in practice for it has been true for years that I occasionally find myself wishing that git status grouped by renamed/added/deleted/modified within staged/unstaged/untracked before the lexical sort).

Sorting staged-changes before no-staged-changes also matches a decision already made in --git output: staged status is the left character, unstaged status is the right character. So when you scan visually, it would work out really nicely - for example (modified from Exa --git documentation):

$ exa --long --git
Permissions Size User Date Modified Git Name
.rw-r--r--   155 ben  25 Nov 17:23   NM unselectable.scss
.rw-r--r--   612 ben  24 Nov 16:04   M- media.scss
.rw-r--r--   260 ben  25 Nov 17:23   -M icon.scss
.rw-r--r--    59 ben  25 Nov 17:23   -N plain-link.scss
.rw-r--r--    96 ben  21 Nov 14:20   -- hoverable.scss

Which corresponds to:

$ git status
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   media.scss
        new file:   unselectable.scss

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   icon.scss
        modified:   unselectable.scss

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        plain-link.scss

P.S. a third reason for making has-staged-changes versus does-not-have-staged-changes as the primary sort is that right before a commit, the most important/salient difference is what's staged - and any deliberate use of the staging area (staging smaller pieces for individual commits instead of basically ignoring the staged/unstaged difference and committing all outstanging changes at once), it's really helpful to have a fast-and-easy-to-visually-parse cue of staged-vs-unstaged.

Sounds excellent to me.