forked from abhinav/git-spice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrebase_continue.go
107 lines (92 loc) · 3.02 KB
/
rebase_continue.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package main
import (
"context"
"errors"
"fmt"
"strings"
"github.com/alecthomas/kong"
"github.com/charmbracelet/log"
"go.abhg.dev/gs/internal/git"
"go.abhg.dev/gs/internal/text"
)
type rebaseContinueCmd struct{}
func (*rebaseContinueCmd) Help() string {
return text.Dedent(`
Continues an ongoing git-spice operation interrupted by
a git rebase after all conflicts have been resolved.
For example, if 'gs upstack restack' gets interrupted
because a conflict arises during the rebase,
you can resolve the conflict and run 'gs rebase continue'
(or its shorthand 'gs rbc') to continue the operation.
The command can be used in place of 'git rebase --continue'
even if a git-spice operation is not currently in progress.
`)
}
func (cmd *rebaseContinueCmd) Run(
ctx context.Context,
log *log.Logger,
opts *globalOptions,
parser *kong.Kong,
) error {
repo, err := git.Open(ctx, ".", git.OpenOptions{
Log: log,
})
if err != nil {
return fmt.Errorf("open repository: %w", err)
}
store, err := ensureStore(ctx, repo, log, opts)
if err != nil {
return err
}
if _, err := repo.RebaseState(ctx); err != nil {
if !errors.Is(err, git.ErrNoRebase) {
return fmt.Errorf("get rebase state: %w", err)
}
return errors.New("no rebase in progress")
}
// Finish the ongoing rebase.
if err := repo.RebaseContinue(ctx); err != nil {
var rebaseErr *git.RebaseInterruptError
if errors.As(err, &rebaseErr) {
var msg strings.Builder
fmt.Fprintf(&msg, "There are more conflicts to resolve.\n")
fmt.Fprintf(&msg, "Resolve them and run the following command again:\n")
fmt.Fprintf(&msg, " gs rebase continue\n")
fmt.Fprintf(&msg, "To abort the remaining operations run:\n")
fmt.Fprintf(&msg, " git rebase --abort\n")
log.Error(msg.String())
}
return err
}
// Once we get here, we have a clean state to continue running
// rebase continuations on.
// However, if any of the continuations encounters another conflict,
// they will clear the continuation list.
// So we'll want to grab the whole list here,
// and push the remainder of it back on if a command fails.
conts, err := store.TakeContinuations(ctx, "gs rebase continue")
if err != nil {
return fmt.Errorf("take rebase continuations: %w", err)
}
for idx, cont := range conts {
log.Debugf("Got rebase continuation: %q (branch: %s)", cont.Command, cont.Branch)
if err := repo.Checkout(ctx, cont.Branch); err != nil {
return fmt.Errorf("checkout branch %q: %w", cont.Branch, err)
}
kctx, err := parser.Parse(cont.Command)
if err != nil {
log.Errorf("Corrupt rebase continuation: %q", cont.Command)
return fmt.Errorf("parse rebase continuation: %w", err)
}
if err := kctx.Run(ctx); err != nil {
// If the command failed, it has already printed the
// rebase message, and appended its continuations.
// We'll append the remainder.
if err := store.AppendContinuations(ctx, "rebase continue", conts[idx+1:]...); err != nil {
return fmt.Errorf("append rebase continuations: %w", err)
}
return err
}
}
return nil
}