Skip to content

Latest commit

 

History

History
257 lines (210 loc) · 9.54 KB

git-summary.org

File metadata and controls

257 lines (210 loc) · 9.54 KB

git-summary - Git repository status summary

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.

Table of Contents

Use

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 the git-summary:add-repo and git-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 the fd 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.

Implementation

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]
        &timestamp= ($chain:segment[git-timestamp])
        &branch= ($chain:segment[git-branch])
      ]
    } catch e {
      put [
        &repo= (tilde-abbr $r)
        &status= [(styled '['(to-string $e)']' red)]
        &ts= ""
        &timestamp= ""
        &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
}