statusMatrix
Efficiently get the status of multiple files at once.
| param | type [= default] | description | 
|---|---|---|
| fs | FsClient | a file system client | 
| dir | string | The working tree directory path | 
| gitdir | string = join(dir, '.git') | The git directory path | 
| ref | string = 'HEAD' | Optionally specify a different commit to compare against the workdir and stage instead of the HEAD | 
| filepaths | Array<string> = ['.'] | Limit the query to the given files and directories | 
| filter | function(string): boolean | Filter the results to only those whose filepath matches a function. | 
| cache | object | a cache object | 
| ignored | boolean = false | include ignored files in the result | 
| return | Promise<Array<StatusRow>> | Resolves with a status matrix, described below. | 
type StatusRow = Array<(string|number)>;
The returned StatusMatrix is admittedly not the easiest format to read.
However it conveys a large amount of information in dense format that should make it easy to create reports about the current state of the repository;
without having to do multiple, time-consuming isomorphic-git calls.
My hope is that the speed and flexibility of the function will make up for the learning curve of interpreting the return value.
// get the status of all the files in 'src'
let status = await git.statusMatrix({
  fs,
  dir: '/tutorial',
  filter: f => f.startsWith('src/')
})
console.log(status)
// get the status of all the JSON and Markdown files
let status = await git.statusMatrix({
  fs,
  dir: '/tutorial',
  filter: f => f.endsWith('.json') || f.endsWith('.md')
})
console.log(status)
The result is returned as a 2D array. The outer array represents the files and/or blobs in the repo, in alphabetical order. The inner arrays describe the status of the file: the first value is the filepath, and the next three are integers representing the HEAD status, WORKDIR status, and STAGE status of the entry.
// example StatusMatrix
[
  ["a.txt", 0, 2, 0], // new, untracked
  ["b.txt", 0, 2, 2], // added, staged
  ["c.txt", 0, 2, 3], // added, staged, with unstaged changes
  ["d.txt", 1, 1, 1], // unmodified
  ["e.txt", 1, 2, 1], // modified, unstaged
  ["f.txt", 1, 2, 2], // modified, staged
  ["g.txt", 1, 2, 3], // modified, staged, with unstaged changes
  ["h.txt", 1, 0, 1], // deleted, unstaged
  ["i.txt", 1, 0, 0], // deleted, staged
  ["j.txt", 1, 2, 0], // deleted, staged, with unstaged-modified changes (new file of the same name)
  ["k.txt", 1, 1, 0], // deleted, staged, with unstaged changes (new file of the same name)
]
- The HEAD status is either absent (0) or present (1).
- The WORKDIR status is either absent (0), identical to HEAD (1), or different from HEAD (2).
- The STAGE status is either absent (0), identical to HEAD (1), identical to WORKDIR (2), or different from WORKDIR (3).
type Filename      = string
type HeadStatus    = 0 | 1
type WorkdirStatus = 0 | 1 | 2
type StageStatus   = 0 | 1 | 2 | 3
type StatusRow     = [Filename, HeadStatus, WorkdirStatus, StageStatus]
type StatusMatrix  = StatusRow[]
Think of the natural progression of file modifications as being from HEAD (previous) -> WORKDIR (current) -> STAGE (next). Then HEAD is "version 1", WORKDIR is "version 2", and STAGE is "version 3". Then, imagine a "version 0" which is before the file was created. Then the status value in each column corresponds to the oldest version of the file it is identical to. (For a file to be identical to "version 0" means the file is deleted.)
Here are some examples of queries you can answer using the result:
Q: What files have been deleted?
const FILE = 0, WORKDIR = 2
const filenames = (await statusMatrix({ dir }))
  .filter(row => row[WORKDIR] === 0)
  .map(row => row[FILE])
Q: What files have unstaged changes?
const FILE = 0, WORKDIR = 2, STAGE = 3
const filenames = (await statusMatrix({ dir }))
  .filter(row => row[WORKDIR] !== row[STAGE])
  .map(row => row[FILE])
Q: What files have been modified since the last commit?
const FILE = 0, HEAD = 1, WORKDIR = 2
const filenames = (await statusMatrix({ dir }))
  .filter(row => row[HEAD] !== row[WORKDIR])
  .map(row => row[FILE])
Q: What files will NOT be changed if I commit right now?
const FILE = 0, HEAD = 1, STAGE = 3
const filenames = (await statusMatrix({ dir }))
  .filter(row => row[HEAD] === row[STAGE])
  .map(row => row[FILE])
For reference, here are all possible combinations:
| HEAD | WORKDIR | STAGE | git status --shortequivalent | 
|---|---|---|---|
| 0 | 0 | 0 | `` | 
| 0 | 0 | 3 | AD | 
| 0 | 2 | 0 | ?? | 
| 0 | 2 | 2 | A | 
| 0 | 2 | 3 | AM | 
| 1 | 0 | 0 | D | 
| 1 | 0 | 1 | D | 
| 1 | 0 | 3 | MD | 
| 1 | 1 | 0 | D+?? | 
| 1 | 1 | 1 | `` | 
| 1 | 1 | 3 | MM | 
| 1 | 2 | 0 | D+?? | 
| 1 | 2 | 1 | M | 
| 1 | 2 | 2 | M | 
| 1 | 2 | 3 | MM | 
Tip: If you need a clean slate, expand and run this snippet to clean up the file system.
window.fs = new LightningFS('fs', { wipe: true })
window.pfs = window.fs.promises
console.log('done')