Display a status summary of a user-defined list of git repositories.
This file is written in literate programming style, to make it easy to explain. See git-summary.elv for the generated file.
This module allows displaying a status summary of a user-defined list of git repositories. I find it useful to see at a glance the status and how recently I have made changes to my commonly used repositories.
Install the elvish-modules
package using epm (you can put these statements in your rc.elv
file as well, to automatically install the package upon startup if needed):
use epm
epm:install &silent-if-installed github.com/zzamboni/elvish-modules
In your rc.elv
, load this module:
use github.com/zzamboni/elvish-modules/git-summary
This module includes the git-summary:summary-status
function, which provides a status summary of git repositories, using the git-combined
, git-branch
and git-timestamp
segments of the chain prompt theme. The list is presented in reverse chronological order according to their latest git commit. I use this to get a quick summary of the status of my most commonly used repositories. The repositories to display can be provided in multiple ways (if more than one is specified, the first one found is used):
- Default behavior when no arguments nor options are given: read from a JSON file specified in
$git-summary:summary-repos-file
(default value:~/.elvish/package-data/elvish-themes/git-summary-repos.json
). The contents of this file can be manipulated using thegit-summary:add-repo
andgit-summary:remove-repo
functions (see example below). - As arguments to the function, e.g.
git-summary:summary-status dir1 dir2
. - All the git repos inside your home directory:
git-summary:summary-status &all
. Note: this uses thefd
command by default, can be changed by storing the new function in$git-summary:find-all-user-repos
. Default value:git-summary:find-all-user-repos = { fd -H -I -t d '^.git$' ~ | each $path:dir~ }
- In combination with any of the above, the
&only-dirty
option can be used to only display repositories which are not clean.
You can add or remove directories to the list by using the git-summary:add-repo
and git-summary:remove-repo
functions. By default these functions add/remove the current directory, but you can also specify others. Example:
[~]─> cd ~/.elvish
[~/.elvish]─[⎇ master]─> git-summary:add-repo
Repo /Users/taazadi1/.elvish added to the list
[~/.elvish]─[⎇ master]─> git-summary:add-repo ~/.emacs.d ~/.hammerspoon
Repo /Users/taazadi1/.emacs.d added to the list
Repo /Users/taazadi1/.hammerspoon added to the list
[~/.elvish]─[⎇ master]─> git-summary:summary-status
[2020-05-25] [OK] [⎇ master] ~/.elvish
[2020-05-27] [OK] [⎇ master] ~/.emacs.d
[2020-05-22] [OK] [⎇ master] ~/.hammerspoon
[~/.elvish]─[⎇ master]─> git-summary:summary-status ~/.elvish/lib/github.com/zzamboni/*
[2020-05-09] [OK] [⎇ master] ~/.elvish/lib/github.com/zzamboni/elvish-completions
[2020-05-08] [OK] [⎇ master] ~/.elvish/lib/github.com/zzamboni/elvish-modules
[2020-05-22] [●] [⎇ master] ~/.elvish/lib/github.com/zzamboni/elvish-themes
[~/.elvish]─[⎇ master]─> git-summary:summary-status &only-dirty ~/.elvish/lib/github.com/zzamboni/*
[2020-05-22] [●] [⎇ master] ~/.elvish/lib/github.com/zzamboni/elvish-themes
Note that this module automatically starts gitstatusd
in the background (courtesy of the elvish-gitstatus module). By default, gitstatusd
stays running and is reused the next time you call the git-summary:summary-status
function. If you prefer to kill gitstatusd
after every run, you can set $git-summary:stop-gitstatusd-after-use
to $true
.
Load some libraries.
use path
use github.com/zzamboni/elvish-modules/spinners
We use the segments from the Chain theme for the display, so we load that module.
use github.com/zzamboni/elvish-themes/chain
The list of repositories read from the file is cached in $git-summary:summary-repos
.
var summary-repos = []
Default function used to find repositories for the &all
option.
var find-all-user-repos-fn = {
fd -H -I -t d '^.git$' ~ | each $path:dir~
}
Default location of the file where the list of repositories to show is stored.
var repos-file = ~/.elvish/package-data/elvish-themes/git-summary-repos.json
Whether to stop gitstatusd
after using it.
var stop-gitstatusd-after-use = $false
We define a couple of functions to read and write $git-summary:summary-repos
from disk.
fn -write-summary-repos {
mkdir -p (path:dir $repos-file)
to-json [$summary-repos] > $repos-file
}
fn -read-summary-repos {
try {
set summary-repos = (from-json < $repos-file)
} catch {
set summary-repos = []
}
}
The git-summary:gather-data
function collects the data from a given set of repositories.
fn gather-data {|repos|
each {|r|
try {
cd $r
chain:-parse-git &with-timestamp
var status = [($chain:segment[git-combined])]
put [
&repo= (tilde-abbr $r)
&status= $status
&ts= $chain:last-status[timestamp]
×tamp= ($chain:segment[git-timestamp])
&branch= ($chain:segment[git-branch])
]
} catch e {
put [
&repo= (tilde-abbr $r)
&status= [(styled '['(to-string $e)']' red)]
&ts= ""
×tamp= ""
&branch= ""
]
}
} $repos
if $stop-gitstatusd-after-use {
# Temporarily disable background job notifications
var old-notify-bg-job-success = $notify-bg-job-success
set notify-bg-job-success = $false
chain:gitstatus:stop
sleep 0.01
set notify-bg-job-success = $old-notify-bg-job-success
}
}
The git-summary:summary-status
function is the main entry point to display the status of the configured repos.
fn summary-status {|@repos &all=$false &only-dirty=$false|
var prev = $pwd
# Determine how to sort the output. This only happens in newer
# versions of Elvish (where the order function exists)
use builtin
var order-cmd~ = $all~
if (has-key $builtin: order~) {
set order-cmd~ = { order &less-than={|a b| <s $a[ts] $b[ts] } &reverse }
}
# Read repo list from disk, cache in $git-summary:summary-repos
-read-summary-repos
# Determine the list of repos to display:
# 1) If the &all option is given, find them
if $all {
spinners:run &title="Finding all git repos" &style=blue {
set repos = [($find-all-user-repos-fn)]
}
}
# 2) If repos is not given nor defined through &all, use $git-summary:summary-repos
if (eq $repos []) {
set repos = $summary-repos
}
# 3) If repos is specified, just use it
# Produce the output
spinners:run &title="Gathering repo data" &style=blue { gather-data $repos } | order-cmd | each {|r|
var status-display = $r[status]
if (or (not $only-dirty) (not-eq $status-display [])) {
if (eq $status-display []) {
var color = (chain:-segment-style git-combined)
set status-display = [(chain:-colorized "[" $color) (styled OK green) (chain:-colorized "]" $color)]
}
var @status = $r[timestamp] ' ' (all $status-display) ' ' $r[branch]
echo &sep="" $@status ' ' (chain:-colorized $r[repo] (chain:-segment-style git-repo))
}
}
cd $prev
}
The git-summary:add-repo
and git-summary:remove-repo
functions can be used to add/remove directories from the summary list. If no directories are given as arguments, they operate on the current directory.
fn add-repo {|@dirs|
if (eq $dirs []) {
set dirs = [ $pwd ]
}
-read-summary-repos
each {|d|
if (has-value $summary-repos $d) {
echo (styled "Repo "$d" is already in the list" yellow)
} else {
set summary-repos = [ $@summary-repos $d ]
echo (styled "Repo "$d" added to the list" green)
}
} $dirs
-write-summary-repos
}
fn remove-repo {|@dirs|
if (eq $dirs []) {
set dirs = [ $pwd ]
}
-read-summary-repos
var @new-repos = (each {|d|
if (not (has-value $dirs $d)) { put $d }
} $summary-repos)
each {|d|
if (has-value $summary-repos $d) {
echo (styled "Repo "$d" removed from the list." green)
} else {
echo (styled "Repo "$d" was not on the list" yellow)
}
} $dirs
set summary-repos = $new-repos
-write-summary-repos
}