diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-29 17:14:38 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-29 17:14:38 +0300 |
| commit | e344b94d455712d9e753ef235bc6ff0e76fd91f2 (patch) | |
| tree | 993268290262595107441b2a303e7797674d3e8c | |
| parent | b076c5e2ac5403de28c5d0ba44fddf216f3034ce (diff) | |
fix(vq): restore version-prefix parsing and stash no-op handling
| -rw-r--r-- | internal/release/release.go | 17 | ||||
| -rw-r--r-- | internal/release/release_tags_test.go | 23 | ||||
| -rw-r--r-- | internal/sync/git_operations.go | 16 | ||||
| -rw-r--r-- | internal/sync/git_operations_test.go | 67 | ||||
| -rw-r--r-- | internal/sync/sync.go | 5 |
5 files changed, 121 insertions, 7 deletions
diff --git a/internal/release/release.go b/internal/release/release.go index 0c8b33f..c6eca97 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -11,6 +11,7 @@ import ( "sort" "strconv" "strings" + "unicode" "codeberg.org/snonux/gitsyncer/internal/aitool" "codeberg.org/snonux/gitsyncer/internal/httpclient" @@ -182,9 +183,21 @@ func compareVersions(v1, v2 string) int { } func parseVersionPart(part string) int { - n, err := strconv.Atoi(part) + start := 0 + if part != "" && (part[0] == '+' || part[0] == '-') { + start = 1 + } + + end := start + for end < len(part) && unicode.IsDigit(rune(part[end])) { + end++ + } + if end == start { + return 0 + } + + n, err := strconv.Atoi(part[:end]) if err != nil { - // Keep current behavior for non-numeric segments. return 0 } diff --git a/internal/release/release_tags_test.go b/internal/release/release_tags_test.go index eb6d581..521fbe8 100644 --- a/internal/release/release_tags_test.go +++ b/internal/release/release_tags_test.go @@ -49,6 +49,25 @@ func TestParseVersionPart_NonNumericReturnsZero(t *testing.T) { } } +func TestParseVersionPart_MixedNumericPrefix(t *testing.T) { + t.Parallel() + + testCases := []struct { + in string + want int + }{ + {in: "1beta", want: 1}, + {in: "-2x", want: -2}, + {in: "+12rc1", want: 12}, + } + + for _, tc := range testCases { + if got := parseVersionPart(tc.in); got != tc.want { + t.Fatalf("parseVersionPart(%q) = %d, want %d", tc.in, got, tc.want) + } + } +} + func TestCompareVersions_NonNumericSegmentsFollowZeroFallback(t *testing.T) { t.Parallel() @@ -59,6 +78,10 @@ func TestCompareVersions_NonNumericSegmentsFollowZeroFallback(t *testing.T) { if got := compareVersions("v2.beta", "v2.0"); got != 0 { t.Fatalf("compareVersions(v2.beta, v2.0) = %d, want 0", got) } + + if got := compareVersions("v1beta.2", "v1.1"); got != 1 { + t.Fatalf("compareVersions(v1beta.2, v1.1) = %d, want 1", got) + } } func runGit(t *testing.T, repoPath string, args ...string) string { diff --git a/internal/sync/git_operations.go b/internal/sync/git_operations.go index 1acfb95..e5c68d9 100644 --- a/internal/sync/git_operations.go +++ b/internal/sync/git_operations.go @@ -36,10 +36,20 @@ func checkForMergeConflicts(repoPath string) (bool, string, error) { return hasConflicts, statusStr, nil } -// stashChanges stashes uncommitted changes -func stashChanges(repoPath string) error { +// stashChanges stashes uncommitted changes. +// Returns true only when a stash entry was created. +func stashChanges(repoPath string) (bool, error) { fmt.Println(" Stashing uncommitted changes...") - return gitCommand(repoPath, "stash", "push", "-m", "gitsyncer-auto-stash").Run() + output, err := gitCommand(repoPath, "stash", "push", "-m", "gitsyncer-auto-stash").CombinedOutput() + if err != nil { + return false, err + } + + if strings.Contains(string(output), "No local changes to save") { + return false, nil + } + + return true, nil } // popStash attempts to pop the stash (used in defer) diff --git a/internal/sync/git_operations_test.go b/internal/sync/git_operations_test.go index b80bf0e..4ca630d 100644 --- a/internal/sync/git_operations_test.go +++ b/internal/sync/git_operations_test.go @@ -120,6 +120,73 @@ func TestPopStash_RestoresStashedChanges(t *testing.T) { } } +func TestStashChanges_NoStashEntryReturnsFalse(t *testing.T) { + repoPath := t.TempDir() + + runGit(t, repoPath, "init") + runGit(t, repoPath, "config", "user.name", "Test User") + runGit(t, repoPath, "config", "user.email", "test@example.com") + + trackedFile := filepath.Join(repoPath, "tracked.txt") + if err := os.WriteFile(trackedFile, []byte("committed\n"), 0o644); err != nil { + t.Fatalf("write tracked file: %v", err) + } + runGit(t, repoPath, "add", "tracked.txt") + runGit(t, repoPath, "commit", "-m", "initial") + + untrackedFile := filepath.Join(repoPath, "untracked.txt") + if err := os.WriteFile(untrackedFile, []byte("new\n"), 0o644); err != nil { + t.Fatalf("write untracked file: %v", err) + } + + stashed, err := stashChanges(repoPath) + if err != nil { + t.Fatalf("stashChanges() unexpected error: %v", err) + } + if stashed { + t.Fatal("stashChanges() = true, want false when no stash entry is created") + } + + if stashList := runGit(t, repoPath, "stash", "list"); stashList != "" { + t.Fatalf("expected no stash entries, got %q", stashList) + } +} + +func TestHandleWorkingDirectoryState_NoStashEntryReturnsNotStashed(t *testing.T) { + workDir := t.TempDir() + repoName := "repo" + repoPath := filepath.Join(workDir, repoName) + + if err := os.MkdirAll(repoPath, 0o755); err != nil { + t.Fatalf("mkdir repo path: %v", err) + } + + runGit(t, repoPath, "init") + runGit(t, repoPath, "config", "user.name", "Test User") + runGit(t, repoPath, "config", "user.email", "test@example.com") + + trackedFile := filepath.Join(repoPath, "tracked.txt") + if err := os.WriteFile(trackedFile, []byte("committed\n"), 0o644); err != nil { + t.Fatalf("write tracked file: %v", err) + } + runGit(t, repoPath, "add", "tracked.txt") + runGit(t, repoPath, "commit", "-m", "initial") + + untrackedFile := filepath.Join(repoPath, "untracked.txt") + if err := os.WriteFile(untrackedFile, []byte("new\n"), 0o644); err != nil { + t.Fatalf("write untracked file: %v", err) + } + + s := &Syncer{workDir: workDir, repoName: repoName} + stashed, err := s.handleWorkingDirectoryState() + if err != nil { + t.Fatalf("handleWorkingDirectoryState() unexpected error: %v", err) + } + if stashed { + t.Fatal("handleWorkingDirectoryState() = true, want false when stash push creates no entry") + } +} + func runGit(t *testing.T, repoPath string, args ...string) string { t.Helper() diff --git a/internal/sync/sync.go b/internal/sync/sync.go index 949cb43..048803e 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -367,10 +367,11 @@ func (s *Syncer) handleWorkingDirectoryState() (bool, error) { } // If we have uncommitted changes but no conflicts, try to stash them - if err := stashChanges(repoPath); err != nil { + stashed, err := stashChanges(repoPath) + if err != nil { return false, fmt.Errorf("failed to stash changes: %w", err) } - return true, nil + return stashed, nil } // checkoutBranch checks out a branch, creating it if necessary |
