--- a/internal/cli/cmd_snap.go
+++ b/internal/cli/cmd_snap.go
@@ -1,180 +1,180 @@
package cli
import (
"bufio"
"fmt"
"os"
"strings"
"arche/internal/gitcompat"
"arche/internal/tui"
"arche/internal/wc"
"github.com/spf13/cobra"
)
var (
snapInteractive bool
snapSign bool
snapSignKey string
snapAmend bool
snapNoAdvance bool
)
var snapCmd = &cobra.Command{
Use: "snap [message]",
Aliases: []string{"commit"
+, "snapshot"
},
Short: "Finalise the working copy draft into a named commit",
Long: `Snapshot the current working directory into the draft commit (optionally
setting a message), finalise it, and create a new empty draft as the next
HEAD. The snapped commit keeps its change ID; only the content hash changes
if files were modified since the last snap.
With --interactive (-i) you are shown each hunk and asked whether to include
it in this snap. Unselected hunks remain as working-copy changes in the new
draft.`,
RunE: func(cmd *cobra.Command, args []string) error {
r := openRepo()
defer r.Close()
msg := strings.Join(args, " ")
if msg == "" {
msg = promptMessage()
}
if msg == "" {
return fmt.Errorf("aborting snap: empty commit message")
}
w := wc.New(r)
if snapNoAdvance {
w.NoAutoAdvance = true
}
if snapSign || r.Cfg.Sign.Auto {
w.SignKey = r.Cfg.Sign.KeyFile
if snapSignKey != "" {
w.SignKey = snapSignKey
}
}
if snapAmend {
amended, amendedID, err := w.Amend(msg)
if err != nil {
return err
}
signedLabel := ""
if amended.CommitSig != nil {
signedLabel = " [signed]"
}
fmt.Printf("Amended %s - %s%s\n", "ch:"+amended.ChangeID, amended.Message, signedLabel)
fmt.Printf(" %x\n", amendedID[:8])
return nil
}
if snapInteractive {
diffs, err := w.ComputeWorkingDiffs()
if err != nil {
return err
}
if len(diffs) == 0 {
return fmt.Errorf("nothing to snap: working copy is clean")
}
var items []tui.HunkItem
for _, fhd := range diffs {
for hi, h := range fhd.Hunks {
items = append(items, tui.HunkItem{
FilePath: fhd.Path,
HunkIdx: hi,
TotalHunksInFile: len(fhd.Hunks),
Hunk: h,
})
}
}
sel, err := tui.RunHunkSelector(items, "include in snap")
if err != nil {
return err
}
if sel.Cancelled {
fmt.Fprintln(os.Stderr, "Interactive snap cancelled.")
return nil
}
perFile := make(map[string][]bool)
idx := 0
for _, fhd := range diffs {
n := len(fhd.Hunks)
perFile[fhd.Path] = sel.Selected[idx : idx+n]
idx += n
}
snapped, snappedID, err := w.SnapSelectedHunks(msg, diffs, perFile)
if err != nil {
return err
}
signedLabelI := ""
if snapped.CommitSig != nil {
signedLabelI = " [signed]"
}
fmt.Printf("Snapped %s - %s%s\n", "ch:"+snapped.ChangeID, snapped.Message, signedLabelI)
fmt.Printf(" %x\n", snappedID[:8])
if r.Cfg.Git.Enabled {
gitHash, err := gitcompat.MirrorCommit(r.Root, r, snappedID)
if err != nil {
fmt.Fprintf(os.Stderr, "arche: git mirror failed: %v\n", err)
} else if gitHash != "" {
fmt.Printf(" git: %s\n", gitHash[:8])
}
}
head, _ := r.Head()
fmt.Printf("Working copy now at %s (draft)\n", head)
return nil
}
snapped, snappedID, err := w.Snap(msg)
if err != nil {
return err
}
signedLabel := ""
if snapped.CommitSig != nil {
signedLabel = " [signed]"
}
fmt.Printf("Snapped %s - %s%s\n", "ch:"+snapped.ChangeID, snapped.Message, signedLabel)
fmt.Printf(" %x\n", snappedID[:8])
if r.Cfg.Git.Enabled {
gitHash, err := gitcompat.MirrorCommit(r.Root, r, snappedID)
if err != nil {
fmt.Fprintf(os.Stderr, "arche: git mirror failed: %v\n", err)
} else if gitHash != "" {
fmt.Printf(" git: %s\n", gitHash[:8])
}
}
head, _ := r.Head()
fmt.Printf("Working copy now at %s (draft, empty)\n", head)
return nil
},
}
func promptMessage() string {
fmt.Print("Commit message: ")
sc := bufio.NewScanner(os.Stdin)
sc.Scan()
return strings.TrimSpace(sc.Text())
}
func init() {
snapCmd.Flags().BoolVarP(&snapInteractive, "interactive", "i", false, "interactively select hunks to include in this snap")
snapCmd.Flags().BoolVar(&snapSign, "sign", false, "sign the commit with your SSH key")
snapCmd.Flags().StringVar(&snapSignKey, "key", "", "path to SSH private key to use for signing (default: auto-detect)")
snapCmd.Flags().BoolVar(&snapAmend, "amend", false, "amend the current commit in-place and auto-rebase downstream draft dependents")
snapCmd.Flags().BoolVar(&snapNoAdvance, "no-advance", false, "disable bookmark auto-advance for this snap (overrides config)")
}