summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-28 09:40:56 +0300
committerPaul Buetow <paul@buetow.org>2026-05-28 09:40:56 +0300
commit5d8937e22cbaf6e96d7fbd5811de33244b5d5c87 (patch)
tree7dd2fceb9b419996204dc03b5de1f62c58074935
parentdfff6b976888b5799f7d24b28796cb1d3b5941e4 (diff)
fix(sync): correct local tag peel syntax in getTagCommitHash (kp)
-rw-r--r--internal/sync/git_operations.go15
-rw-r--r--internal/sync/git_operations_test.go75
2 files changed, 86 insertions, 4 deletions
diff --git a/internal/sync/git_operations.go b/internal/sync/git_operations.go
index 6619349..08eea34 100644
--- a/internal/sync/git_operations.go
+++ b/internal/sync/git_operations.go
@@ -181,7 +181,7 @@ func handleTagConflict(repoPath, remote string, output []byte) error {
func getTagCommitHash(repoPath, tag, source string) (string, error) {
var cmd *exec.Cmd
if source == "local" {
- cmd = gitCommand(repoPath, "rev-parse", tag+"^{\\}")
+ cmd = gitCommand(repoPath, "rev-parse", tag+"^{}")
} else {
cmd = gitCommand(repoPath, "ls-remote", "--tags", source, tag)
}
@@ -191,8 +191,17 @@ func getTagCommitHash(repoPath, tag, source string) (string, error) {
return "", err
}
- hash := strings.Fields(string(output))[0]
- return hash, nil
+ return parseTagHashOutput(output, tag, source)
+}
+
+// parseTagHashOutput extracts the first whitespace-separated field from output
+// as the commit hash for a tag, returning an error when output is empty.
+func parseTagHashOutput(output []byte, tag, source string) (string, error) {
+ fields := strings.Fields(string(output))
+ if len(fields) == 0 {
+ return "", fmt.Errorf("no hash found for tag %s from %s", tag, source)
+ }
+ return fields[0], nil
}
// checkoutExistingBranch tries to checkout an existing branch
diff --git a/internal/sync/git_operations_test.go b/internal/sync/git_operations_test.go
index 03ddb81..e696fb7 100644
--- a/internal/sync/git_operations_test.go
+++ b/internal/sync/git_operations_test.go
@@ -1,6 +1,10 @@
package sync
-import "testing"
+import (
+ "os/exec"
+ "strings"
+ "testing"
+)
func TestGitCommand_SetsDir(t *testing.T) {
cmd := gitCommand("/tmp/example-repo", "status")
@@ -17,3 +21,72 @@ func TestGitCommand_LeavesDirEmptyForGlobalCommands(t *testing.T) {
t.Fatalf("expected empty dir for global command, got %q", cmd.Dir)
}
}
+
+func TestParseTagHashOutput_EmptyOutput(t *testing.T) {
+ for _, in := range [][]byte{nil, []byte(""), []byte(" \n\t \n")} {
+ hash, err := parseTagHashOutput(in, "v1.0.0", "origin")
+ if err == nil {
+ t.Fatalf("expected error for empty output %q, got hash %q", in, hash)
+ }
+ if hash != "" {
+ t.Fatalf("expected empty hash for empty output, got %q", hash)
+ }
+ if !strings.Contains(err.Error(), "v1.0.0") || !strings.Contains(err.Error(), "origin") {
+ t.Fatalf("expected error to mention tag and source, got %v", err)
+ }
+ }
+}
+
+func TestParseTagHashOutput_ReturnsFirstField(t *testing.T) {
+ hash, err := parseTagHashOutput([]byte("abc123\trefs/tags/v1.0.0\n"), "v1.0.0", "origin")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if hash != "abc123" {
+ t.Fatalf("expected hash %q, got %q", "abc123", hash)
+ }
+}
+
+func TestGetTagCommitHash_LocalTagPeelsToCommitHash(t *testing.T) {
+ repoPath := t.TempDir()
+
+ runGit(t, repoPath, "init")
+ runGit(t, repoPath, "-c", "user.name=Test", "-c", "user.email=test@example.com", "commit", "--allow-empty", "-m", "initial commit")
+ runGit(t, repoPath, "tag", "-a", "v1.0.0", "-m", "release v1.0.0")
+
+ headHash := runGit(t, repoPath, "rev-parse", "HEAD")
+ tagHash, err := getTagCommitHash(repoPath, "v1.0.0", "local")
+ if err != nil {
+ t.Fatalf("expected local tag hash lookup to succeed, got error: %v", err)
+ }
+ if tagHash != headHash {
+ t.Fatalf("expected peeled local tag hash %q, got %q", headHash, tagHash)
+ }
+}
+
+func TestGetTagCommitHash_LocalTagMissingReturnsError(t *testing.T) {
+ repoPath := t.TempDir()
+ runGit(t, repoPath, "init")
+
+ tagHash, err := getTagCommitHash(repoPath, "v9.9.9", "local")
+ if err == nil {
+ t.Fatalf("expected error for missing local tag, got hash %q", tagHash)
+ }
+ if tagHash != "" {
+ t.Fatalf("expected empty hash for missing local tag, got %q", tagHash)
+ }
+}
+
+func runGit(t *testing.T, repoPath string, args ...string) string {
+ t.Helper()
+
+ cmd := exec.Command("git", args...)
+ cmd.Dir = repoPath
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("git %v failed: %v\n%s", args, err, string(output))
+ }
+
+ return strings.TrimSpace(string(output))
+}