feat(lanes): git login from the dashboard, merge and conflict resolution (#45)

Part A — a fetch/pull/push against a remote that needs a login no longer
dies on "terminal prompts disabled": the server answers 409 EGITAUTH with
the host, the git bar asks for a username + password/access token and
resends the same action. AgentHub keeps its OWN credential store
(<CLAUDE_HOME>/agenthub-git-credentials/, 0700/0600), one login per host,
shared by every user of the install, managed from Settings -> Git
credentials. A login is only sent to one of the repo's own remote hosts,
saved only after git accepted it, never returned by any route, and reaches
git through an exclusive credential helper (never argv).

Part B — merge a branch (or the diverged upstream) from the git bar; a
merge that stops on conflicts lists them in the Changes panel with Keep
mine / Take theirs / Mark resolved, then Complete merge or Abort merge
(confirm gate). Clean tree, repo-root lane and an existing ref only;
pull/switch/hunk actions are blocked mid-merge; staging a conflicted file
with markers left is refused. Rebase/reset/stash stay out.
This commit is contained in:
2026-09-25 10:44:40 +07:00
parent 605ca9d65e
commit f514602697
30 changed files with 2005 additions and 59 deletions
+26 -3
View File
@@ -552,12 +552,22 @@ the Changes panel carries the per-repo git **writes** the dashboard offers (`POS
| **Create** | `git branch <name>` — never switches | the branch already exists |
| **Delete** | `git branch -d <name>` | it is protected (`main`, `master`, `development`, `develop`), current, held by another worktree, or has unmerged commits |
| **New lane here** | opens **Add lane** prefilled with this repo and the selected branch | — |
| **Merge into current** | `git merge --no-edit <branch>` (after a confirm) | the tree is dirty, the lane is a subdirectory of its repo, or a merge is already in progress |
| **Merge `<upstream>`** | the same, offered when the branch has diverged (ahead AND behind) | as above |
These sit in the panel rather than in the lane card's ⋯ menu deliberately: that menu holds
the rare and dangerous (`reset worktree`), and these are the ordinary moves a
lane needs several times an hour. The boring half of git is the whole list on
purpose — there is no merge, rebase, reset or stash button, because a dashboard
click is the wrong place to start an operation whose recovery needs a terminal.
lane needs several times an hour. There is no rebase, reset or stash button,
because a dashboard click is the wrong place to start an operation whose
recovery needs a terminal. **Merge** is the one exception (issue #45), because
its recovery is a button too: a merge that stops on conflicts puts a banner at
the top of the Changes panel — **Complete merge** (enabled once nothing is
conflicted) and **Abort merge** (two clicks; back to where the branch was) —
and lists the conflicted files in their own **Merge conflicts** group with
**Keep mine**, **Take theirs** and **Mark resolved** (after editing the
`<<<<<<<`/`>>>>>>>` markers out in the file editor; refused while any are
left). Pull and branch switch are disabled until the merge is finished or
aborted.
`pull --ff-only` and the dirty-tree guard on `switch` are what keep either from
inventing a merge commit or a conflict underneath a running agent. **Create**
deliberately does not switch: the usual reason to make a branch from a lane is
@@ -571,6 +581,19 @@ what was staged.
A failed action prints git's own `stderr` verbatim — `! [rejected] … (non-fast-forward)`
says more than any sentence the dashboard could write.
The exception is a remote that wants a **login** (issue #45). The dashboard
runs git without a terminal, so where a shell would prompt, the bar opens a
form instead — "Git needs a login for `<host>`": username, password or access
token (on Gitea: Settings → Applications → Generate token), and **Remember for
everyone using this AgentHub** (on by default) — and resends the same action
with it. A saved login lives in AgentHub's own store, one per host, shared by
every user of the install, and is managed from Settings → **Git credentials**;
it is saved only after git accepted it. While AgentHub holds a login for a
repo's host, that login is the only one git is given for the command — the
machine's own credential helpers are left out, so a stale token there cannot
win. Over plain `http://` on a LAN address the form warns that the token
crosses the network unencrypted. See `docs/API.md` § Git credentials.
### The Changes panel
To the right of the lane detail sits a collapsible **Changes** card (its state