Repository: seachicken/gh-poi (go)

Issue:
Branch not deleted when checked out
**Describe the bug**
To Reproduce:
1. Create any PR
2. Squash and merge PR
3. `gh poi`
4. Can't delete PR branch in local

**Your Environment**
- OS: macOS Monterey
- gh (Check with `gh --version`): 2.14.4
- gh-poi (For gh v2.6+ check with `gh extension list`): v0.6.2


Pull request description:
Delete merged branches even when they have untracked uncommitted changes
Fixes #77 

Patch:
diff --git a/poi.go b/poi.go
index 1ab7886..62ef4d1 100644
--- a/poi.go
+++ b/poi.go
@@ -57,6 +57,12 @@ type (
 		Url     string
 		Author  string
 	}
+
+	UncommittedChange struct {
+		X    string
+		Y    string
+		Path string
+	}
 )
 
 const (
@@ -137,8 +143,10 @@ func GetBranches(remote Remote, connection Connection, dryRun bool) ([]Branch, e
 
 	branches = applyPullRequest(branches, prs, connection)
 
-	uncommittedChanges, err := connection.GetUncommittedChanges()
-	if err != nil {
+	var uncommittedChanges []UncommittedChange
+	if changes, err := connection.GetUncommittedChanges(); err == nil {
+		uncommittedChanges = toUncommittedChange(splitLines(changes))
+	} else {
 		return nil, err
 	}
 
@@ -413,7 +421,19 @@ func findMatchedPullRequest(branchName string, prs []PullRequest, prNumbers map[
 	return results
 }
 
-func checkDeletion(branches []Branch, uncommittedChanges string) []Branch {
+func toUncommittedChange(changes []string) []UncommittedChange {
+	results := []UncommittedChange{}
+	for _, change := range changes {
+		results = append(results, UncommittedChange{
+			string(change[0]),
+			string(change[1]),
+			string(change[3:]),
+		})
+	}
+	return results
+}
+
+func checkDeletion(branches []Branch, uncommittedChanges []UncommittedChange) []Branch {
 	results := []Branch{}
 	for _, branch := range branches {
 		branch.State = getDeleteStatus(branch, uncommittedChanges)
@@ -422,8 +442,15 @@ func checkDeletion(branches []Branch, uncommittedChanges string) []Branch {
 	return results
 }
 
-func getDeleteStatus(branch Branch, uncommittedChanges string) BranchState {
-	if branch.Head && len(uncommittedChanges) > 0 {
+func getDeleteStatus(branch Branch, uncommittedChanges []UncommittedChange) BranchState {
+	hasTrackedChanges := false
+	for _, change := range uncommittedChanges {
+		if !change.IsUntracked() {
+			hasTrackedChanges = true
+			break
+		}
+	}
+	if branch.Head && hasTrackedChanges {
 		return NotDeletable
 	}
 
@@ -644,3 +671,7 @@ func splitLines(text string) []string {
 func (b Branch) IsDetached() bool {
 	return detachedBranchNameRegex.MatchString(b.Name)
 }
+
+func (uc *UncommittedChange) IsUntracked() bool {
+	return uc.Y == "?"
+}


Test patch:
diff --git a/poi_test.go b/poi_test.go
index 90fb77d..b0d8343 100644
--- a/poi_test.go
+++ b/poi_test.go
@@ -390,7 +390,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithoutADefaultBranch(t *testin
 	}, actual)
 }
 
-func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
+func Test_ShouldNotDeletableWhenBranchHasModifiedUncommittedChanges(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -445,6 +445,61 @@ func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
 	}, actual)
 }
 
+func Test_ShouldBeDeletableWhenBranchHasUntrackedUncommittedChanges(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	s := conn.Setup(ctrl).
+		CheckRepos(nil, nil).
+		GetRemoteNames("origin", nil, nil).
+		GetSshConfig("github.com", nil, nil).
+		GetRepoNames("origin", nil, nil).
+		GetBranchNames("main_@issue1", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
+		GetLog([]conn.LogStub{
+			{BranchName: "main", Filename: "main"}, {BranchName: "issue1", Filename: "issue1"},
+		}, nil, nil).
+		GetAssociatedRefNames([]conn.AssociatedBranchNamesStub{
+			{Oid: "a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0", Filename: "issue1"},
+			{Oid: "6ebe3d30d23531af56bd23b5a098d3ccae2a534a", Filename: "main_issue1"},
+		}, nil, nil).
+		GetPullRequests("issue1Merged", nil, nil).
+		GetUncommittedChanges("?? new.txt", nil, nil).
+		GetConfig([]conn.ConfigStub{
+			{BranchName: "branch.main.merge", Filename: "main"},
+			{BranchName: "branch.issue1.merge", Filename: "issue1"},
+		}, nil, nil).
+		CheckoutBranch(nil, nil)
+	remote, _ := GetRemote(s.Conn)
+
+	actual, _ := GetBranches(remote, s.Conn, false)
+
+	assert.Equal(t, []Branch{
+		{
+			false, "issue1", false,
+			[]string{
+				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
+			},
+			[]PullRequest{
+				{
+					"issue1", Merged, false, 1,
+					[]string{
+						"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
+					},
+					"https://github.com/owner/repo/pull/1", "owner",
+				},
+			},
+			Deletable,
+		},
+		{
+			true, "main", true,
+			[]string{},
+			[]PullRequest{},
+			NotDeletable,
+		},
+	}, actual)
+}
+
 func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()