VirtusLab / git-machete

Probably the sharpest git repository organizer & rebase/merge workflow automation tool you've ever seen
MIT License
915 stars 53 forks source link

Add JSON (machine readable) status output #1210

Open asford opened 8 months ago

asford commented 8 months ago

Overview

git machete is a mega-useful workflow tool, partially because is nicely separates the current branch state (the git repo state) from a declared target state (the machete config).

It'd be real useful to be able to overlay workflow helpers or lightweight views onto git machete; which would be much easier if there was an easily-parsed output format.

Most interactions with machete can be represented as:

  1. Call git machete status to get the branch tree and branch status information.
  2. Inspect the branch tree and (maybe) current repo/remote state to know what to do.
  3. Invoke some git or git machete command to do the needful.

Therefor, we can implement most integrations by only adding output git machete status. Semi-obviously, this should just be a json form of the status output.

In my experience, this best represented as an adjacency based structure akin to the output of the git machete show commands. In dataclasses:

# single-character status, equivalent to ASCII output
class Status(str, enum.Enum):
    IN_SYNC = "o"
    MERGED = "m"
    OUT_OF_SYNC = "x"
    WEIRD_FORK = "?"

@attr.define
class MacheteConfig:
    # primary data from status - must have to reconstruct full status
    # all tracked branches
    tracked: list[str]

    # branch to parent mapping
    # a branch is only present if it's non-root and has a parent
    up: dict[str, str]

    # branch status
    status: dict[str, Status]

    # branch custom annotations
    anno: dict[str, str]

    # derived data from status - useful views but not strictly needed
    current: str | None
    roots: list[str] # all roots in the tree
    non_root: list[str] # all non-roots in the tree

    # show output, keyed by branch
    down: dict[str, list[str]]
    first: dict[str, str]
    last: dict[str, str]
    next: dict[str, str]
    prev: dict[str, str]
    root: dict[str, str]

This can be semi-trivially generated during machete's current status implementation. The easiest implementation might be to just add a --json flag to git machete status?

Happy to open a PR for comments if you're open to it.

PawelLipski commented 8 months ago

Wowow that's a novel idea... could you elaborate on how what kind of integrations do you consider? so far I'd rather expected that the tool is only ever used by human users directly, and not by scripts (other than maybe some scripts like git hooks or shell completion for git-machete itself, hence git-machete defines some plumbing commands of its own).

asford commented 8 months ago

Honestly, the simplest case is just github/gitlab integration.

We have an internal machete-to-github PR management tool that wraps machete. It slightly pre-dates the in-built integration in machete and it's about ~%60 identical to what y'all have implemented.

However, as we all know, PR flows are ... highly opinionated ... matters; there differences in behavior between our tool and your upstream. E.g. the behavior wrt comment updates, how this are updated on push, etc. Neither implementation is "correct", just different colors of the bike shed. Right now our tool reaches into machete's internals to populate the datastructure above, but this is obviously undesirable.

Having a plumbing-level command for status output would let us easily integrate with machete; defer to the builtins when we can, but add in our own weird commenting and workflow flavor on top. Status is, as you know, one of the immensely tricky problems to solve because you're doing reflog introspection. 😨

In slightly-less-simple cases, we have actions workflows built on machete that do things like auto-merge of stack machete trees. These workflows run on github and benefit immensely from have verbose status information about a tree. For example, we would slide-out and push in a workflow if-and-only-if the (root, merged, child) branches are an up-to-date machete stack, and the root is main and the parent and child branch are tagged.

In the way-far-afield-future I could imagine a status-json interface supporting a VSCode extension for machete. This would only require that the extension call machete status and then do the needful rendering of a status tree. Various update commands could just be simple dispatches down to appropriate machete and/or git commands.

PawelLipski commented 8 months ago

I wasn't aware you've got such complicated machinery on top of git-machete 😯 sure, feel free to open the PR! I'm currently busy with GitLab integration btw (#1189), this is gonna take me while (even though the API is strikingly similar to GitHub's) :/

Also, as you probably know, we've got an IntelliJ plugin that covers most of the features of CLI (even traverse; but notably, GitHub integration is still missing). There's unfortunately no easy way to quickly port it to a VSCode extension :/