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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

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

Let a change request gain its issue links after it is opened

bdf6a0de1ad493c9c4650a27c926767b06621a70 · Keenan Nemetz · 14d ago · parent 183d48e5

Let a change request gain its issue links after it is opened

A link is usually discovered after the request exists: the issue turns up
while reviewing, or while writing the landing note. Without an edit path
the only options were to open a second request or leave the issue behind,
which is the drift the link was meant to stop.

`vole change update <id> --closes <issue>...` replaces the list and
`--no-closes` clears it, alongside the existing `--head`, in one command
and one metadata commit. An unchanged list is not an edit, so the branch
does not move, and the ids are checked against the issues tree at that
point as well as at creation — a link added later is exactly where a typo
would otherwise hide until the issue quietly never closed.

Only an open request may be relinked. A merged request's list records what
the merge did, and a closed one is not going to run, so both refuse.

metadata/request_write.go +69

@@ -478,3 +478,72 @@ func loadFrom(files map[string][]byte, id string) (*Request, error) {
478 478 }
479 479 return nil, fmt.Errorf("requests: no request %s", id)
480 480 }
481
482 // SetRequestCloses replaces the issues a request promises to close. An open request is
483 // the only thing that can land, so it is the only thing whose links are worth
484 // changing: a merged request's list is a record of what the merge did, and a closed
485 // one is not going to run.
486 //
487 // The ids are checked against the issues tree here as well as at creation, because a
488 // request may be linked long after it was opened, and a typo at that point would
489 // otherwise surface as an issue that quietly never closes.
490 func SetRequestCloses(g *git.Repo, id string, closes []string, actor string) (*Request, error) {
491 closes, problems := parseCloses(closes, "request")
492 if len(problems) > 0 {
493 return nil, fmt.Errorf("%s", strings.Join(problems, "; "))
494 }
495 if len(closes) > 0 {
496 issues, err := LoadIssues(g, Ref)
497 if err != nil {
498 return nil, err
499 }
500 known := make(map[string]bool, len(issues))
501 for _, iss := range issues {
502 known[iss.ID] = true
503 }
504 for _, want := range closes {
505 if !known[want] {
506 return nil, fmt.Errorf("requests: closes %s, and there is no such issue", want)
507 }
508 }
509 }
510 var out *Request
511 base := fmt.Sprintf("%s/%s/", RequestsDir, id)
512 _, err := git.CommitFiles(g, Ref, func(files map[string][]byte) error {
513 r, err := loadFrom(files, id)
514 if err != nil {
515 return err
516 }
517 if r.State != "open" {
518 return fmt.Errorf("requests: %s is %s, so its links cannot change", id, r.State)
519 }
520 if equalStrings(r.Closes, closes) {
521 out = r
522 return ErrNoChange
523 }
524 r.Closes = closes
525 raw, err := renderRequest(r)
526 if err != nil {
527 return err
528 }
529 files[base+requestName] = raw
530 out = r
531 return nil
532 }, fmt.Sprintf("request: %s closes %d issue(s)", id, len(closes)), actor)
533 if errors.Is(err, ErrNoChange) {
534 return out, nil
535 }
536 return out, err
537 }
538
539 func equalStrings(a, b []string) bool {
540 if len(a) != len(b) {
541 return false
542 }
543 for i := range a {
544 if a[i] != b[i] {
545 return false
546 }
547 }
548 return true
549 }