Skip to content

Events

tt-git emits an event when something happens in the repository: a commit, a push, a checkout. A plugin can react in two ways: an [[events]] hook, which runs a command for each event, or a subscription, which streams events to a program that’s already running.

Every event is a JSON envelope with the event’s name and its data:

{"event": "commit.created", "data": {"oid": "3f9c2e1…", "amend": false, "summary": "Fix the parser"}}

Add an [[events]] entry to the manifest for each event you want:

tt-git-plugin.toml
[[events]]
on = "push.completed"
command = ["node", "notify.mjs"]
[[events]]
on = "commit.created"
command = ["node", "notify.mjs"]

Each time the event happens, tt-git runs the command as a fresh process with:

  • TTGIT_PLUGIN_EVENT: the event name.
  • TTGIT_PLUGIN_EVENT_JSON: the envelope, {"event": …, "data": …}.
  • The usual environment and context, with invocation_source set to event.
notify.mjs
import { execFileSync } from "node:child_process";
const { event, data } = JSON.parse(process.env.TTGIT_PLUGIN_EVENT_JSON ?? "{}");
const message =
event === "push.completed" ? `🚀 pushed ${data.branch ?? "HEAD"}`
: event === "commit.created" ? `✔ committed ${String(data.oid ?? "").slice(0, 7)} ${data.summary ?? ""}`
: `event ${event}`;
execFileSync(process.env.TTGIT_BIN_PATH, ["ui", "notify", message]);

Hooks start a process per event, so they suit events that happen now and then. Their runs show up in the plugin log.

A program that’s already running, such as a pane or a background helper started by [[startup]], can subscribe instead and read events as they happen. From the command line:

Terminal window
tt-git events # every event, one JSON object per line
tt-git events --event push.completed --event fetch.completed

Or over the socket, with events.subscribe. The reply is {"subscribed": …}, and then one envelope per line for as long as the connection stays open:

→ {"id":"req_1","method":"events.subscribe","params":{"events":["push.completed"]}}
← {"id":"req_1","result":{"subscribed":true}}
← {"event":"push.completed","data":{"branch":"main"}}

Leave out events to get every event.

tt-git events is also the easiest way to see what your hooks will receive.

EventWhendata
app.startedtt-git started, after the startup commands. Also when you open another worktree, which starts tt-git over there.{version}
repo.openedThe repository was opened, right after app.started.{path, name}
head.changedHEAD moved, from tt-git or outside it. This includes switching between two branches at the same commit, and undo, which sends no event of its own.{from, from_branch, to, branch}
branch.checked_outYou checked out a branch, or created one (without a worktree). Checking out a commit from the graph with HEAD detached sends only head.changed; stashing first also sends stash.created.{branch, created, tracking?}. created is true for a new branch, including tracking branches made for a remote-only branch; tracking is the remote branch then.
branch.createdA branch was created, from a commit, a tag, a branch or a stash.{branch, start, worktree}. worktree is the new worktree’s path, or null.
branch.deletedA branch was deleted, locally or on a remote.{branch, remote}. remote is null for a local branch.
branch.renamedA branch was renamed.{from, to}
tag.createdA tag was made.{tag, target, annotated}. target is the commit or ref it was made at.
tag.pushedA tag was pushed.{tag}
tag.deletedA tag was deleted, locally or here and on a remote.{tag, remote}. remote is null when only the local tag was deleted.
commit.createdA commit was made or amended.{oid, amend, summary}
fetch.completedA fetch finished.{}
pull.completedA pull finished.{branch, head}
push.completedA push finished.{branch}
files.stagedFiles were staged.{paths}, or "all" for stage all. partial: true when only some hunks or lines were staged.
files.unstagedFiles were unstaged.{paths}, plus partial: true as above.
files.discardedA file’s changes were discarded.{path}, plus partial: true as above.
merge.completedA merge finished.{target, head}
rebase.completedA rebase finished.{onto, head}
cherry_pick.completedA cherry-pick finished.{picked, summary, head}
stash.createdA stash was made.{message} (null when you left it empty)
stash.appliedA stash was applied or popped.{stash, pop}, where stash is like stash@{0}
stash.droppedA stash was dropped.{stash}
worktree.removedA worktree was removed from the Worktrees tab.{path, branch, force}. branch is what it had checked out (null if detached); force is true when it was removed with its changes.

When you finish a merge, rebase or cherry-pick that stopped on conflicts (C in the status pane), the matching *.completed event fires with {head, continued: true}.