Commit e208dfc
authored
Create/update stack on remote during sync (#156)
* Create/update the remote stack on sync and fix false "Stack synced"
`gh stack sync` reported "Stack synced" even when it had not created or
updated the stack object on GitHub. After running `gh stack init` to
adopt existing branches and then opening PRs outside the CLI, `gh stack
sync` detected the open PRs and printed "Stack synced" — but no stack had
ever been created on the server.
There were two distinct bugs:
1. Sync never reconciled the remote stack object. `runSync` called
`syncStackPRs`, which only *reads* PR state and links PRs to local
branches; it never called the create/update path. So the branches were
rebased and pushed and the PRs were detected, but the stack on GitHub
was never created.
2. The final message was unconditional. `runSync` always printed "Stack
synced", which is supposed to mean "the stack object on GitHub now
reflects the local stack" — something that can only be true when two or
more open PRs exist and the remote stack was actually created/updated.
Fix
Reconcile the remote stack from sync, and make the closing message reflect
what actually happened.
* cmd/sync.go
- Add a reconciliation step (5b) after PR-state sync: when the stack has
two or more open PRs, link them into a stack on GitHub via the new
`syncRemoteStack` helper. It inspects existing stacks first and:
- short-circuits quietly when a remote stack already lists exactly
these PRs (records the ID, prints "Stack already up to date on
GitHub") so routine syncs don't issue a redundant, misleading
update;
- otherwise delegates to `syncStack` to create a new stack, adopt an
untracked one, or update a partially-formed one.
Sync never opens PRs — that remains `gh stack submit`'s job.
- Replace the unconditional "Stack synced" with a result-driven message:
"Stack synced" when the remote stack object was created/updated/in
sync, otherwise "Branches synced" (fewer than two PRs, stacked PRs
unavailable, a cross-stack divergence, or no GitHub client).
- Update the command's long description to document the stack-object
step and the two possible closing messages.
* cmd/submit.go
- Thread a `synced bool` return through the existing, tested stack
helpers so sync can tell whether the remote stack object now matches
local: `syncStack`, `createNewStack`, and `updateStack` now return
`bool`; `adoptRemoteStack` returns `(handled, synced)`; and
`handleCreate422` returns `bool` (true only when the PRs are already
stacked together). Extract the shared `stackPRNumbers` helper.
- This is additive: submit's single call site ignores the new return
value, so submit's behavior, output, and tests are unchanged. Reusing
these helpers (instead of duplicating the 404/422 handling in sync)
keeps the create/adopt/update logic in one tested place.
Tests
* cmd/sync_test.go — six new cases covering the reconciliation matrix:
- TestSync_CreatesRemoteStackWhenPRsExist: open PRs but no remote stack
-> CreateStack is called and the new ID is persisted to the stack file;
output contains "Stack created on GitHub" and "Stack synced".
- TestSync_AdoptsExistingEqualRemoteStack: a matching remote stack ->
no create/update, ID recorded, "Stack synced".
- TestSync_UpdatesPartialRemoteStack: a subset stack -> UpdateStack with
the full PR list, "Stack synced".
- TestSync_FewerThanTwoPRs_BranchesSynced: one PR -> no stack API calls,
"Branches synced", not "Stack synced".
- TestSync_StacksUnavailable_BranchesSynced: 404 on create -> warns,
"Branches synced".
- TestSync_PRsSpanMultipleStacks_BranchesSynced: PRs across two stacks ->
divergence warning, no create/update, "Branches synced".
Docs
Document the new stack-object step and the "Stack synced" vs "Branches
synced" distinction in:
- README.md
- docs/src/content/docs/reference/cli.md
- skills/gh-stack/SKILL.md
- docs/src/content/docs/introduction/overview.md
- docs/src/content/docs/guides/stacked-prs.md
- docs/src/content/docs/guides/workflows.md
* Address PR review: one ListStacks per sync, command-neutral guidance
Two follow-ups from the #156 review (both flagged optional / non-blocking).
1. Remove the redundant ListStacks round-trip on sync's create path.
syncRemoteStack fetched the stack list for its already-up-to-date
short-circuit, then delegated to syncStack -> adoptRemoteStack, which
listed the stacks again — two GETs on the first-sync-create and
membership-changed paths. Refactor adoptRemoteStack into a list-accepting
reconcileUntrackedStack(cfg, client, s, prNumbers, stacks): syncStack now
fetches the list once and passes it down, and syncRemoteStack reuses the
list it already fetched. Net: exactly one ListStacks per sync. This also
drops the (handled, synced) tuple. Submit's behavior is unchanged.
2. Make the divergence / dropped-PR guidance command-neutral. The shared
helper emitted submit-specific wording ("reconcile them before
submitting", "...then `gh stack submit`") that is now reachable from
`gh stack sync`. Reword to "reconcile them first" and drop the trailing
`gh stack submit` so it reads correctly from either command.
Tests: assert exactly one ListStacks on the create path and that the
divergence guidance is not submit-specific.
* increment skill file version
* Simplify sync reconciliation: reuse syncStack instead of a parallel path
Review feedback noted the change felt heavier than the fix warranted.
The weight came from `syncRemoteStack` (cmd/sync.go), a near-duplicate of
submit's `syncStack` — same <2-PR guard, ListStacks, and update/create
dispatch — that existed only to add an "already up to date" short-circuit.
That one optimization is what spawned the second entry point, the
pre-fetched-list threading, and the double-ListStacks it then required.
Collapse it to a single reconciliation path:
- Remove `syncRemoteStack`; `gh stack sync` now calls the shared
`syncStack` directly. One path, one ListStacks per sync.
- Fold `createNewStack` into `reconcileUntrackedStack` (renamed from
`adoptRemoteStack`) so it returns a single `synced bool` instead of a
`(handled, synced)` tuple and owns its own ListStacks again.
- Inline `stackPRNumbers` back into `syncStack` (it was only extracted to
share with the now-removed `syncRemoteStack`).
- Drop the now-unused `strconv`/`github` imports from cmd/sync.go.
Behavior note: a routine re-sync of an already-tracked stack now prints
"Stack updated on GitHub with N PRs" instead of "Stack already up to date
on GitHub". This is accurate (sync does PUT the current state) and matches
submit. The "Stack synced" / "Branches synced" summary is unchanged, and
submit's behavior is unchanged.1 parent 754d190 commit e208dfc
9 files changed
Lines changed: 357 additions & 50 deletions
File tree
- cmd
- docs/src/content/docs
- guides
- introduction
- reference
- skills/gh-stack
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
323 | 323 | | |
324 | 324 | | |
325 | 325 | | |
326 | | - | |
| 326 | + | |
| 327 | + | |
327 | 328 | | |
328 | 329 | | |
329 | 330 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
690 | 690 | | |
691 | 691 | | |
692 | 692 | | |
693 | | - | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
694 | 698 | | |
695 | 699 | | |
696 | 700 | | |
| |||
703 | 707 | | |
704 | 708 | | |
705 | 709 | | |
706 | | - | |
| 710 | + | |
707 | 711 | | |
708 | 712 | | |
709 | 713 | | |
710 | | - | |
711 | | - | |
| 714 | + | |
712 | 715 | | |
713 | 716 | | |
714 | 717 | | |
715 | 718 | | |
716 | 719 | | |
717 | 720 | | |
718 | | - | |
719 | | - | |
720 | | - | |
721 | | - | |
722 | | - | |
| 721 | + | |
723 | 722 | | |
724 | 723 | | |
725 | | - | |
726 | | - | |
727 | | - | |
728 | | - | |
729 | | - | |
730 | | - | |
731 | | - | |
732 | | - | |
733 | | - | |
734 | | - | |
735 | | - | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
736 | 733 | | |
737 | 734 | | |
738 | 735 | | |
739 | 736 | | |
740 | | - | |
| 737 | + | |
741 | 738 | | |
742 | 739 | | |
743 | 740 | | |
744 | 741 | | |
745 | 742 | | |
746 | 743 | | |
747 | 744 | | |
748 | | - | |
| 745 | + | |
749 | 746 | | |
750 | 747 | | |
751 | | - | |
| 748 | + | |
752 | 749 | | |
753 | 750 | | |
754 | 751 | | |
755 | 752 | | |
756 | | - | |
| 753 | + | |
757 | 754 | | |
758 | 755 | | |
759 | 756 | | |
760 | 757 | | |
761 | 758 | | |
762 | 759 | | |
763 | 760 | | |
764 | | - | |
765 | | - | |
766 | | - | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
767 | 764 | | |
768 | 765 | | |
769 | 766 | | |
| |||
777 | 774 | | |
778 | 775 | | |
779 | 776 | | |
780 | | - | |
781 | | - | |
| 777 | + | |
782 | 778 | | |
783 | 779 | | |
784 | 780 | | |
| |||
800 | 796 | | |
801 | 797 | | |
802 | 798 | | |
803 | | - | |
| 799 | + | |
| 800 | + | |
804 | 801 | | |
805 | 802 | | |
806 | 803 | | |
| |||
809 | 806 | | |
810 | 807 | | |
811 | 808 | | |
812 | | - | |
| 809 | + | |
813 | 810 | | |
814 | 811 | | |
815 | 812 | | |
| |||
818 | 815 | | |
819 | 816 | | |
820 | 817 | | |
821 | | - | |
| 818 | + | |
822 | 819 | | |
823 | 820 | | |
824 | 821 | | |
| |||
827 | 824 | | |
828 | 825 | | |
829 | 826 | | |
830 | | - | |
| 827 | + | |
831 | 828 | | |
832 | 829 | | |
| 830 | + | |
833 | 831 | | |
834 | 832 | | |
835 | 833 | | |
836 | 834 | | |
837 | | - | |
| 835 | + | |
| 836 | + | |
838 | 837 | | |
839 | 838 | | |
840 | 839 | | |
841 | 840 | | |
842 | | - | |
| 841 | + | |
843 | 842 | | |
844 | 843 | | |
845 | 844 | | |
846 | 845 | | |
847 | 846 | | |
848 | | - | |
| 847 | + | |
849 | 848 | | |
850 | 849 | | |
851 | 850 | | |
852 | 851 | | |
853 | | - | |
| 852 | + | |
854 | 853 | | |
855 | 854 | | |
| 855 | + | |
856 | 856 | | |
857 | 857 | | |
| 858 | + | |
858 | 859 | | |
859 | 860 | | |
860 | 861 | | |
| |||
863 | 864 | | |
864 | 865 | | |
865 | 866 | | |
866 | | - | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
867 | 871 | | |
868 | 872 | | |
869 | 873 | | |
| |||
872 | 876 | | |
873 | 877 | | |
874 | 878 | | |
875 | | - | |
| 879 | + | |
876 | 880 | | |
877 | 881 | | |
878 | 882 | | |
879 | 883 | | |
880 | | - | |
| 884 | + | |
881 | 885 | | |
882 | 886 | | |
883 | 887 | | |
884 | 888 | | |
885 | 889 | | |
886 | | - | |
| 890 | + | |
887 | 891 | | |
888 | 892 | | |
889 | 893 | | |
890 | 894 | | |
| 895 | + | |
891 | 896 | | |
892 | 897 | | |
893 | 898 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
40 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
41 | 50 | | |
42 | 51 | | |
43 | 52 | | |
| |||
220 | 229 | | |
221 | 230 | | |
222 | 231 | | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
223 | 244 | | |
224 | 245 | | |
225 | 246 | | |
| |||
316 | 337 | | |
317 | 338 | | |
318 | 339 | | |
319 | | - | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
320 | 348 | | |
321 | 349 | | |
322 | 350 | | |
| |||
0 commit comments