diff --git a/cmd/tag/new_tag.go b/cmd/tag/new_tag.go index 8f7b5ed..3e00bcf 100644 --- a/cmd/tag/new_tag.go +++ b/cmd/tag/new_tag.go @@ -42,6 +42,7 @@ package tag import ( "bytes" + "errors" "fmt" "os/exec" "regexp" @@ -53,6 +54,8 @@ import ( "github.com/tonbiattack/git-plus/internal/ui" ) +const initialVersionTag = "v0.0.0" + var ( tagMessage string // タグメッセージ(アノテーテッドタグ用) tagPush bool // 作成後に自動的にリモートへプッシュするフラグ @@ -60,6 +63,7 @@ var ( tagRelease bool // プッシュ後に自動的にGitHubリリースを作成するフラグ tagReleaseDraft bool // リリースをドラフトとして作成するフラグ tagReleasePrerelease bool // リリースをプレリリースとして作成するフラグ + errNoGitTags = errors.New("git repository has no tags") ) // newTagCmd は new-tag コマンドの定義です。 @@ -85,9 +89,13 @@ var newTagCmd = &cobra.Command{ // 最新タグを取得 currentTag, err := getLatestTag() if err != nil { - fmt.Println("エラー: 最新タグの取得に失敗しました") - fmt.Println("タグが存在しない可能性があります。最初のタグを手動で作成してください。") - return err + if errors.Is(err, errNoGitTags) { + fmt.Printf("既存のタグが見つからないため、初期タグ %s から新しいタグを計算します。\n", initialVersionTag) + currentTag = initialVersionTag + } else { + fmt.Println("エラー: 最新タグの取得に失敗しました") + return err + } } // バージョンを解析 @@ -197,11 +205,34 @@ func getLatestTag() (string, error) { cmd := exec.Command("git", "describe", "--tags", "--abbrev=0") output, err := cmd.Output() if err != nil { + if isNoTagsDescribeError(err) { + return "", errNoGitTags + } return "", err } return strings.TrimSpace(string(output)), nil } +// isNoTagsDescribeError は git describe の結果が「タグが存在しない」場合を判定します。 +// +// パラメータ: +// - err: git describe 実行時に発生したエラー +// +// 戻り値: +// - bool: エラーがタグ未作成によるものなら true +func isNoTagsDescribeError(err error) bool { + if err == nil { + return false + } + + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + stderr := string(exitErr.Stderr) + return strings.Contains(stderr, "No names found, cannot describe anything.") + } + return false +} + // extractVersion はタグからバージョン番号を抽出します。 // // パラメータ: diff --git a/cmd/tag/new_tag_test.go b/cmd/tag/new_tag_test.go index 9e2dc7f..c59f6a8 100644 --- a/cmd/tag/new_tag_test.go +++ b/cmd/tag/new_tag_test.go @@ -1,6 +1,8 @@ package tag import ( + "errors" + "os/exec" "testing" "github.com/tonbiattack/git-plus/cmd" @@ -346,3 +348,45 @@ func TestVersionTypeAliases(t *testing.T) { } } } + +// TestIsNoTagsDescribeError は git describe でタグが存在しない場合のエラー判定をテストします +func TestIsNoTagsDescribeError(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "detects no tags error", + err: &exec.ExitError{ + Stderr: []byte("fatal: No names found, cannot describe anything.\n"), + }, + want: true, + }, + { + name: "ignores other exit errors", + err: &exec.ExitError{ + Stderr: []byte("fatal: not a git repository (or any of the parent directories): .git"), + }, + want: false, + }, + { + name: "ignores generic errors", + err: errors.New("some other error"), + want: false, + }, + { + name: "nil error", + err: nil, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isNoTagsDescribeError(tt.err); got != tt.want { + t.Fatalf("isNoTagsDescribeError() = %v, want %v", got, tt.want) + } + }) + } +}