Repository: seachicken/gh-poi (go)

Issue:
Error if detached head branch is present
**Describe the bug**
1. `git clone git@github.com:seachicken/can.git`
2. `cd can`
3. `git checkout 47a4d97`
4. `gh poi`
5. Output:
   ```
   ✕ Fetching pull requests...
   failed to run external command: git
   fatal: ambiguous argument '(HEAD detached at 47a4d97)': unknown revision or path not in the working tree.
   Use '--' to separate paths from revisions, like this:
   'git <command> [<revision>...] -- [<file>...]'
   ```

**Your Environment**
- OS: macOS Monterey
- gh (Check with `gh --version`): 2.6.0
- gh-poi (For gh v2.6+ check with `gh extension list`): 0.4.1


Pull request description:
Avoid error when there is a detached branch
Fixes #44 

Patch:
diff --git a/conn/command.go b/conn/command.go
index 7f7b559..e974b5f 100644
--- a/conn/command.go
+++ b/conn/command.go
@@ -151,12 +151,13 @@ func run(name string, args []string) (string, error) {
 
 	err := cmd.Start()
 	if err != nil {
-		return "", fmt.Errorf("failed to run external command: %s", name)
+		return "", fmt.Errorf("failed to run external command: %s, args: %v", name, args)
 	}
 	cmd.Wait()
 
 	if stderr.Len() > 0 {
-		return "", fmt.Errorf("failed to run external command: %s\n%s", name, stderr.String())
+		return "", fmt.Errorf("failed to run external command: %s, args: %v\n%s",
+			name, args, stderr.String())
 	}
 
 	return stdout.String(), nil
diff --git a/conn/fixtures/git/branch_main_@detached.txt b/conn/fixtures/git/branch_main_@detached.txt
new file mode 100644
index 0000000..6d7fcca
--- /dev/null
+++ b/conn/fixtures/git/branch_main_@detached.txt
@@ -0,0 +1,2 @@
+*,(HEAD detached at a97e963),a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0
+ ,main,6ebe3d30d23531af56bd23b5a098d3ccae2a534a
diff --git a/poi.go b/poi.go
index 1c393de..76766d3 100644
--- a/poi.go
+++ b/poi.go
@@ -71,6 +71,7 @@ const (
 	Open
 )
 
+var detachedBranchNameRegex = regexp.MustCompile(`^\(.+\)`)
 var ErrNotFound = errors.New("not found")
 
 func GetBranches(conn Connection, dryRun bool) ([]Branch, error) {
@@ -240,7 +241,7 @@ func applyCommits(branches []Branch, defaultBranchName string, conn Connection)
 	results := []Branch{}
 
 	for _, branch := range branches {
-		if branch.Name == defaultBranchName {
+		if branch.Name == defaultBranchName || branch.IsDetached() {
 			results = append(results, branch)
 			continue
 		}
@@ -324,6 +325,9 @@ func extractBranchNames(refNames []string) []string {
 func applyPullRequest(branches []Branch, prs []PullRequest, conn Connection) []Branch {
 	prNumbers := map[string]int{}
 	for _, branch := range branches {
+		if branch.IsDetached() {
+			continue
+		}
 		mergeConfig, _ := conn.GetConfig(fmt.Sprintf("branch.%s.merge", branch.Name))
 		if n := getPRNumber(mergeConfig); n > 0 {
 			prNumbers[branch.Name] = n
@@ -627,3 +631,7 @@ func branchNameExists(branchName string, branches []Branch) bool {
 func splitLines(text string) []string {
 	return strings.FieldsFunc(text, func(c rune) bool { return c == '\n' })
 }
+
+func (b Branch) IsDetached() bool {
+	return detachedBranchNameRegex.MatchString(b.Name)
+}


Test patch:
diff --git a/poi_test.go b/poi_test.go
index 01fa7ee..3531701 100644
--- a/poi_test.go
+++ b/poi_test.go
@@ -736,6 +736,46 @@ func Test_ShouldBeNoCommitHistoryWhenTheFirstCommitOfATopicBranchIsAssociatedWit
 	}, actual)
 }
 
+func Test_ShouldBeNoCommitHistoryWhenDetachedBranch(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	s := conn.Setup(ctrl).
+		CheckRepos(nil, nil).
+		GetRemoteNames("origin", nil, nil).
+		GetRepoNames("origin", nil, nil).
+		GetBranchNames("main_@detached", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
+		GetLog([]conn.LogStub{
+			{"main", "main"},
+		}, nil, nil).
+		GetAssociatedRefNames([]conn.AssociatedBranchNamesStub{
+			{"6ebe3d30d23531af56bd23b5a098d3ccae2a534a", "main_issue1"},
+		}, nil, nil).
+		GetPullRequests("notFound", nil, nil).
+		GetUncommittedChanges("", nil, nil).
+		GetConfig([]conn.ConfigStub{
+			{"branch.main.merge", "main"},
+		}, nil, nil)
+
+	actual, _ := GetBranches(s.Conn, false)
+
+	assert.Equal(t, []Branch{
+		{
+			true, "(HEAD detached at a97e963)", false,
+			[]string{},
+			[]PullRequest{},
+			NotDeletable,
+		},
+		{
+			false, "main", true,
+			[]string{},
+			[]PullRequest{},
+			NotDeletable,
+		},
+	}, actual)
+}
+
 func Test_ReturnsAnErrorWhenGetRemoteNamesFails(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()