Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Build out change review and threaded discussions

f6f4b1db24292d1df877680714952a9c568e2b78 · Keenan Nemetz · 15d ago · parent 564acd4b

Build out change review and threaded discussions

- Add Conversation, Files and Runs tabs to change requests. Files uses the
  code-browser shape with a changed-file tree and one selected-file diff;
  Runs lists the head commit's workflow results.
- Show anchored review comments beneath the line, with a collapsed excerpt
  read from the exact blob that was reviewed, and return posted anchors to
  their line note.
- Add comment threads: any comment can be replied to, the UI flattens the
  descendants under one root, reply context links back to the parent, and
  reply targets feed a single composer at the foot of the discussion.
- Collapse reply groups while keeping the root visible, and scroll to the
  composer when Reply is selected.
- Add comment context menus with Reply, Copy text, Copy link, Edit and
  Delete. Add web edit/delete routes with author or admin checks; deletion
  leaves a tombstone so replies keep their thread.
- Support --reply-to in the issue and change CLI commands, and treat a
  missing parent as a tombstone rather than an invalid tree.
- Fix the diff comment gutter, action button colours, reply labels, and
  keep the Files tree and diff pane aligned.

Update metadata validation, web templates, CSS, JavaScript, tests, and the
CLI/change-request/issue docs.

cmd/vole/change.go +18−3

@@ -18,7 +18,7 @@ func changeUsage() string {
18 18 new --title <text> --base <branch> [--head <rev>] [-m <text> | -F <file>]
19 19 list [--state open|closed|all] [--format <fmt>]
20 20 show <id>
21 comment <id> [-m <text> | -F <file>] [--path <file> --line <n> [--side new|old]]
21 comment <id> [-m <text> | -F <file>] [--path <file> --line <n> [--side new|old]] [--reply-to <comment-id>]
22 22 edit-comment <id> <comment-id> [-m <text> | -F <file>]
23 23 rm-comment <id> <comment-id>
24 24 update <id> [--head <rev>]
@@ -51,6 +51,7 @@ func cmdChange(args []string) error {
51 51 {long: "path"},
52 52 {long: "line"},
53 53 {long: "side"},
54 {long: "reply-to"},
54 55 {long: "state"},
55 56 {long: "remote"},
56 57 {long: "format"},
@@ -66,7 +67,7 @@ func cmdChange(args []string) error {
66 67 }
67 68 changeFlags := map[string][]string{
68 69 "new": {"title", "base", "head", "message", "file", "remote", "no-push"},
69 "comment": {"message", "file", "path", "line", "side", "remote", "no-push"},
70 "comment": {"message", "file", "path", "line", "side", "reply-to", "remote", "no-push"},
70 71 "edit-comment": {"message", "file", "remote", "no-push"},
71 72 "rm-comment": {"remote", "no-push"},
72 73 "list": {"state", "format", "remote"},
@@ -198,11 +199,21 @@ func cmdChange(args []string) error {
198 199 } else if f.given("line") || f.given("side") {
199 200 return usagef("--line and --side only mean something with --path")
200 201 }
202 replyTo := strings.TrimSpace(f.one("reply-to"))
203 if replyTo != "" && path != "" {
204 return usagef("--reply-to cannot be combined with --path: a reply belongs to its parent thread")
205 }
206 if replyTo != "" {
207 if replyTo, err = client.ResolveRequestCommentID(c, r.ID, replyTo); err != nil {
208 return err
209 }
210 }
201 211 cm, err := metadata.NewRequestComment(body, author, time.Now())
202 212 if err != nil {
203 213 return err
204 214 }
205 215 cm.Anchor = anchor
216 cm.ReplyTo = replyTo
206 217 reapply := func() error { return metadata.AddRequestComment(c.Repo, r.ID, cm, author) }
207 218 if err := reapply(); err != nil {
208 219 return err
@@ -307,8 +318,12 @@ func cmdChange(args []string) error {
307 318 fmt.Printf("\n%s\n", strings.TrimRight(r.Description, "\n"))
308 319 }
309 320 for _, cm := range r.Comments {
321 body := strings.TrimRight(cm.Body, "\n")
322 if cm.Deleted {
323 body = "(comment deleted)"
324 }
310 325 fmt.Printf("\n--- %s %s (%s) ---\n%s", cm.ID, cm.Author,
311 cm.Created.Format(time.RFC3339), strings.TrimRight(cm.Body, "\n")+"\n")
326 cm.Created.Format(time.RFC3339), body+"\n")
312 327 }
313 328 return nil
314 329