Repository: seachicken/gh-poi (go)

Issue:
Does not work when merging PRs with "Merge pull request"
I have 18 local branches and 8 remote branches, every single one (except `main`) having been merged into `main` via a pull request.

When I run `gh poi` in the repo, all branches show in `Not deleted branches` and nothing is deleted.

It is a public organization repo for an organization including only me.  I have confirmed all other `gh` commands and extensions work as designed.

<!-- A clear and concise description of what the bug is. -->

**Your Environment**
- OS: macOS Monterey 12.3.1
- gh (Check with `gh --version`): 2.7.0
- gh-poi (For gh v2.6+ check with `gh extension list`): 0.4.0


Pull request:
Fix "Merge pull request" to delete merged PRs
Fixes #40 

The PR was excluded from the deletion target due to a change in the process of linking PRs and branches, which has been corrected.
Related PR: https://github.com/seachicken/gh-poi/pull/24

Patch:
diff --git a/conn/command.go b/conn/command.go
index a2ef4c5..7f7b559 100644
--- a/conn/command.go
+++ b/conn/command.go
@@ -51,6 +51,13 @@ func (conn *Connection) GetBranchNames() (string, error) {
 	return run("git", args)
 }
 
+func (conn *Connection) GetMergedBranchNames() (string, error) {
+	args := []string{
+		"branch", "--merged",
+	}
+	return run("git", args)
+}
+
 func (conn *Connection) GetLog(branchName string) (string, error) {
 	args := []string{
 		"log", "--first-parent", "--max-count=30", "--format=%H", branchName,
diff --git a/conn/fixtures/git/branchMerged_@main.txt b/conn/fixtures/git/branchMerged_@main.txt
new file mode 100644
index 0000000..f7c0e86
--- /dev/null
+++ b/conn/fixtures/git/branchMerged_@main.txt
@@ -0,0 +1,1 @@
+* main
diff --git a/conn/fixtures/git/branchMerged_@main_issue1.txt b/conn/fixtures/git/branchMerged_@main_issue1.txt
new file mode 100644
index 0000000..b5381ab
--- /dev/null
+++ b/conn/fixtures/git/branchMerged_@main_issue1.txt
@@ -0,0 +1,2 @@
+  issue1
+* main
diff --git a/conn/fixtures/git/branchMerged_empty.txt b/conn/fixtures/git/branchMerged_empty.txt
new file mode 100644
index 0000000..e69de29
diff --git a/conn/fixtures/git/branchMerged_main.txt b/conn/fixtures/git/branchMerged_main.txt
new file mode 100644
index 0000000..6a0bb4a
--- /dev/null
+++ b/conn/fixtures/git/branchMerged_main.txt
@@ -0,0 +1,1 @@
+  main
diff --git a/conn/fixtures/git/log_issue1Merged.txt b/conn/fixtures/git/log_issue1Merged.txt
new file mode 100644
index 0000000..38b85d1
--- /dev/null
+++ b/conn/fixtures/git/log_issue1Merged.txt
@@ -0,0 +1,2 @@
+a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0
+6ebe3d30d23531af56bd23b5a098d3ccae2a534a
diff --git a/conn/fixtures/git/log_main_issue1Merged.txt b/conn/fixtures/git/log_main_issue1Merged.txt
index 951cf6b..f612df3 100644
--- a/conn/fixtures/git/log_main_issue1Merged.txt
+++ b/conn/fixtures/git/log_main_issue1Merged.txt
@@ -1,2 +1,3 @@
-cb197ba87e4ad323b1008c611212deb7da2a4a49
+b8a2645298053fb62ea03e27feea6c483d3fd27e
+a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0
 6ebe3d30d23531af56bd23b5a098d3ccae2a534a
diff --git a/conn/fixtures/git/log_main_issue1SquashAndMerged.txt b/conn/fixtures/git/log_main_issue1SquashAndMerged.txt
new file mode 100644
index 0000000..951cf6b
--- /dev/null
+++ b/conn/fixtures/git/log_main_issue1SquashAndMerged.txt
@@ -0,0 +1,2 @@
+cb197ba87e4ad323b1008c611212deb7da2a4a49
+6ebe3d30d23531af56bd23b5a098d3ccae2a534a
diff --git a/conn/stub.go b/conn/stub.go
index 945eb10..44382e9 100644
--- a/conn/stub.go
+++ b/conn/stub.go
@@ -101,6 +101,17 @@ func (s *Stub) GetBranchNames(filename string, err error, conf *Conf) *Stub {
 	return s
 }
 
+func (s *Stub) GetMergedBranchNames(filename string, err error, conf *Conf) *Stub {
+	s.t.Helper()
+	configure(
+		s.Conn.EXPECT().
+			GetMergedBranchNames().
+			Return(s.readFile("git", "branchMerged", filename), err),
+		conf,
+	)
+	return s
+}
+
 func (s *Stub) GetAssociatedRefNames(stubs []AssociatedBranchNamesStub, err error, conf *Conf) *Stub {
 	s.t.Helper()
 	for _, stub := range stubs {
diff --git a/mocks/poi_mock.go b/mocks/poi_mock.go
index 1486bc7..aa36ed4 100644
--- a/mocks/poi_mock.go
+++ b/mocks/poi_mock.go
@@ -137,6 +137,21 @@ func (mr *MockConnectionMockRecorder) GetLog(branchName interface{}) *gomock.Cal
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLog", reflect.TypeOf((*MockConnection)(nil).GetLog), branchName)
 }
 
+// GetMergedBranchNames mocks base method.
+func (m *MockConnection) GetMergedBranchNames() (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetMergedBranchNames")
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetMergedBranchNames indicates an expected call of GetMergedBranchNames.
+func (mr *MockConnectionMockRecorder) GetMergedBranchNames() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMergedBranchNames", reflect.TypeOf((*MockConnection)(nil).GetMergedBranchNames))
+}
+
 // GetPullRequests mocks base method.
 func (m *MockConnection) GetPullRequests(hostname string, repoNames []string, queryHashes string) (string, error) {
 	m.ctrl.T.Helper()
diff --git a/poi.go b/poi.go
index e07bf54..7b90e91 100644
--- a/poi.go
+++ b/poi.go
@@ -18,6 +18,7 @@ type (
 		GetRemoteNames() (string, error)
 		GetRepoNames(hostname string, repoName string) (string, error)
 		GetBranchNames() (string, error)
+		GetMergedBranchNames() (string, error)
 		GetLog(branchName string) (string, error)
 		GetAssociatedRefNames(oid string) (string, error)
 		GetPullRequests(hostname string, repoNames []string, queryHashes string) (string, error)
@@ -38,6 +39,7 @@ type (
 	Branch struct {
 		Head         bool
 		Name         string
+		IsMerged     bool
 		Commits      []string
 		PullRequests []PullRequest
 		State        BranchState
@@ -100,6 +102,11 @@ func GetBranches(conn Connection, check bool) ([]Branch, error) {
 	var branches []Branch
 	if names, err := conn.GetBranchNames(); err == nil {
 		branches = toBranch(splitLines(names))
+		mergedNames, err := conn.GetMergedBranchNames()
+		if err != nil {
+			return nil, err
+		}
+		branches = applyMerged(branches, extractMergedBranchNames(splitLines(mergedNames)))
 		branches, err = applyCommits(branches, defaultBranchName, conn)
 		if err != nil {
 			return nil, err
@@ -150,6 +157,7 @@ func GetBranches(conn Connection, check bool) ([]Branch, error) {
 		if !branchNameExists(defaultBranchName, branches) {
 			result = append(result, Branch{
 				true, defaultBranchName,
+				false,
 				[]string{},
 				[]PullRequest{},
 				NotDeletable,
@@ -198,6 +206,36 @@ func getPrimaryRemote(remotes []Remote) (Remote, error) {
 	return remotes[0], nil
 }
 
+func extractMergedBranchNames(mergedNames []string) []string {
+	result := []string{}
+	r := regexp.MustCompile(`^[ *]+(.+)`)
+	for _, name := range mergedNames {
+		found := r.FindStringSubmatch(name)
+		if len(found) > 1 {
+			result = append(result, found[1])
+		}
+	}
+	return result
+}
+
+func applyMerged(branches []Branch, mergedNames []string) []Branch {
+	results := []Branch{}
+	for _, branch := range branches {
+		branch.IsMerged = nameExists(branch.Name, mergedNames)
+		results = append(results, branch)
+	}
+	return results
+}
+
+func nameExists(name string, names []string) bool {
+	for _, n := range names {
+		if n == name {
+			return true
+		}
+	}
+	return false
+}
+
 func applyCommits(branches []Branch, defaultBranchName string, conn Connection) ([]Branch, error) {
 	results := []Branch{}
 
@@ -212,7 +250,8 @@ func applyCommits(branches []Branch, defaultBranchName string, conn Connection)
 			return nil, err
 		}
 
-		trimmedOids, err := trimBranch(splitLines(oids), branch.Name, defaultBranchName, conn)
+		trimmedOids, err := trimBranch(splitLines(oids), branch.IsMerged,
+			branch.Name, defaultBranchName, conn)
 		if err != nil {
 			return nil, err
 		}
@@ -224,11 +263,17 @@ func applyCommits(branches []Branch, defaultBranchName string, conn Connection)
 	return results, nil
 }
 
-func trimBranch(oids []string, branchName string, defaultBranchName string, conn Connection) ([]string, error) {
+func trimBranch(oids []string, isMerged bool,
+	branchName string, defaultBranchName string, conn Connection) ([]string, error) {
 	results := []string{}
 	childNames := []string{}
 
 	for i, oid := range oids {
+		if isMerged {
+			results = append(results, oid)
+			break
+		}
+
 		refNames, err := conn.GetAssociatedRefNames(oid)
 		if err != nil {
 			return nil, err
@@ -408,6 +453,7 @@ func toBranch(branchNames []string) []Branch {
 		results = append(results, Branch{
 			head,
 			splitedNames[1],
+			false,
 			[]string{},
 			[]PullRequest{},
 			Unknown,


Test patch:
diff --git a/conn/command_test.go b/conn/command_test.go
index fb7f53e..7dfd571 100644
--- a/conn/command_test.go
+++ b/conn/command_test.go
@@ -32,6 +32,14 @@ func Test_RepoBasic(t *testing.T) {
 		)
 	})
 
+	t.Run("GetMergedBranchNames", func(t *testing.T) {
+		actual, _ := conn.GetMergedBranchNames()
+		assert.Equal(t,
+			stub.readFile("git", "branchMerged", "@main"),
+			actual,
+		)
+	})
+
 	t.Run("GetLog", func(t *testing.T) {
 
 		t.Run("main", func(t *testing.T) {
diff --git a/poi_test.go b/poi_test.go
index 6d26cdc..84159a3 100644
--- a/poi_test.go
+++ b/poi_test.go
@@ -18,6 +18,60 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedPR(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main_issue1", nil, nil).
+		GetLog([]conn.LogStub{
+			{"main", "main_issue1Merged"}, {"issue1", "issue1Merged"},
+		}, nil, nil).
+		GetAssociatedRefNames([]conn.AssociatedBranchNamesStub{
+			{"b8a2645298053fb62ea03e27feea6c483d3fd27e", "main_issue1"},
+			{"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0", "main_issue1"},
+			{"6ebe3d30d23531af56bd23b5a098d3ccae2a534a", "main_issue1"},
+		}, nil, nil).
+		GetPullRequests("issue1Merged", nil, nil).
+		GetUncommittedChanges("", nil, nil).
+		GetConfig([]conn.ConfigStub{
+			{"branch.main.merge", "main"},
+			{"branch.issue1.merge", "issue1"},
+		}, nil, nil)
+
+	actual, _ := GetBranches(s.Conn, false)
+
+	assert.Equal(t, []Branch{
+		{
+			false, "issue1", true,
+			[]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_ShouldBeDeletableWhenBranchesAssociatedWithSquashAndMergedPR(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_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -36,7 +90,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedPR(t *testing.T) {
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -52,7 +106,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedPR(t *testing.T) {
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -60,7 +114,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedPR(t *testing.T) {
 	}, actual)
 }
 
-func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamMergedPR(t *testing.T) {
+func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamSquashAndMergedPR(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -69,6 +123,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamMergedPR(t *testing
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin_upstream", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -87,7 +142,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamMergedPR(t *testing
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -103,7 +158,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamMergedPR(t *testing
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -111,7 +166,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamMergedPR(t *testing
 	}, actual)
 }
 
-func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamMergedPR(t *testing.T) {
+func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamSquashAndMergedPR(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -120,6 +175,7 @@ func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamMergedPR(
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin_upstream", nil, nil).
 		GetBranchNames("@main_forkMain", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"fork/main", "issue1"},
 		}, nil, nil).
@@ -138,7 +194,7 @@ func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamMergedPR(
 
 	assert.Equal(t, []Branch{
 		{
-			false, "fork/main",
+			false, "fork/main", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -154,7 +210,7 @@ func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamMergedPR(
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -171,6 +227,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsFalse(t *testing.
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("main_@issue1", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -190,7 +247,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsFalse(t *testing.
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -206,7 +263,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsFalse(t *testing.
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -223,6 +280,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsTrue(t *testing.T
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("main_@issue1", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -242,7 +300,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsTrue(t *testing.T
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -258,7 +316,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsTrue(t *testing.T
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -275,6 +333,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithoutADefaultBranch(t *testin
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@issue1", nil, nil).
+		GetMergedBranchNames("empty", nil, nil).
 		GetLog([]conn.LogStub{
 			{"issue1", "issue1"},
 		}, nil, nil).
@@ -293,7 +352,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithoutADefaultBranch(t *testin
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -309,7 +368,7 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithoutADefaultBranch(t *testin
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", false,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -326,6 +385,7 @@ func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("main_@issue1", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -345,7 +405,7 @@ func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
 
 	assert.Equal(t, []Branch{
 		{
-			true, "issue1",
+			true, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -361,7 +421,7 @@ func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
 			NotDeletable,
 		},
 		{
-			false, "main",
+			false, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -378,6 +438,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -396,7 +457,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -412,7 +473,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 			NotDeletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -420,7 +481,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 	}, actual)
 }
 
-func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedAndClosedPRs(t *testing.T) {
+func Test_ShouldBeDeletableWhenBranchesAssociatedWithSquashAndMergedAndClosedPRs(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -429,6 +490,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedAndClosedPRs(t *testi
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -447,7 +509,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedAndClosedPRs(t *testi
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -470,7 +532,7 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedAndClosedPRs(t *testi
 			Deletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -487,8 +549,9 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithNotFullyMergedPR(t *testin
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
-			{"main", "main_issue1Merged"}, {"issue1", "issue1CommitAfterMerge"},
+			{"main", "main_issue1SquashAndMerged"}, {"issue1", "issue1CommitAfterMerge"},
 		}, nil, nil).
 		GetAssociatedRefNames([]conn.AssociatedBranchNamesStub{
 			{"cb197ba87e4ad323b1008c611212deb7da2a4a49", "main"},
@@ -507,7 +570,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithNotFullyMergedPR(t *testin
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"b8a2645298053fb62ea03e27feea6c483d3fd27e",
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
@@ -524,7 +587,7 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithNotFullyMergedPR(t *testin
 			NotDeletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -541,6 +604,7 @@ func Test_ShouldNotDeletableWhenDefaultBranchAssociatedWithMergedPR(t *testing.T
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -559,7 +623,7 @@ func Test_ShouldNotDeletableWhenDefaultBranchAssociatedWithMergedPR(t *testing.T
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0",
 			},
@@ -567,7 +631,7 @@ func Test_ShouldNotDeletableWhenDefaultBranchAssociatedWithMergedPR(t *testing.T
 			NotDeletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{
 				{
@@ -592,6 +656,7 @@ func Test_BranchesAndPRsAreNotAssociatedWhenManyLocalCommitsAreAhead(t *testing.
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"},
 			{"issue1", "issue1ManyCommits"}, // return with '--max-count=3'
@@ -612,7 +677,7 @@ func Test_BranchesAndPRsAreNotAssociatedWhenManyLocalCommitsAreAhead(t *testing.
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{
 				"62d5d8280031f607f1db058da959a97f6a8e6d90",
 				"b8a2645298053fb62ea03e27feea6c483d3fd27e",
@@ -622,7 +687,7 @@ func Test_BranchesAndPRsAreNotAssociatedWhenManyLocalCommitsAreAhead(t *testing.
 			NotDeletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -639,6 +704,7 @@ func Test_ShouldBeNoCommitHistoryWhenTheFirstCommitOfATopicBranchIsAssociatedWit
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "main"},
 		}, nil, nil).
@@ -656,13 +722,13 @@ func Test_ShouldBeNoCommitHistoryWhenTheFirstCommitOfATopicBranchIsAssociatedWit
 
 	assert.Equal(t, []Branch{
 		{
-			false, "issue1",
+			false, "issue1", false,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
 		},
 		{
-			true, "main",
+			true, "main", true,
 			[]string{},
 			[]PullRequest{},
 			NotDeletable,
@@ -724,6 +790,22 @@ func Test_ReturnsAnErrorWhenGetBranchNamesFails(t *testing.T) {
 	assert.NotNil(t, err)
 }
 
+func Test_ReturnsAnErrorWhenGetMergedBranchNames(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_issue1", nil, nil).
+		GetMergedBranchNames("@main", errors.New("failed to run external command: git"), nil)
+
+	_, err := GetBranches(s.Conn, false)
+
+	assert.NotNil(t, err)
+}
+
 func Test_ReturnsAnErrorWhenGetLogFails(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
@@ -733,6 +815,7 @@ func Test_ReturnsAnErrorWhenGetLogFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, errors.New("failed to run external command: git"), nil)
@@ -751,6 +834,7 @@ func Test_ReturnsAnErrorWhenGetAssociatedRefNamesFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -773,6 +857,7 @@ func Test_ReturnsAnErrorWhenGetPullRequestsFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -796,6 +881,7 @@ func Test_ReturnsAnErrorWhenGetUncommittedChangesFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
+		GetMergedBranchNames("@main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -824,6 +910,7 @@ func Test_ReturnsAnErrorWhenCheckoutBranchFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("main_@issue1", nil, nil).
+		GetMergedBranchNames("main", nil, nil).
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, nil, nil).
@@ -853,15 +940,15 @@ func Test_DeletingDeletableBranches(t *testing.T) {
 		DeleteBranches(nil, conn.NewConf(&conn.Times{N: 1}))
 
 	branches := []Branch{
-		{false, "issue1", []string{}, []PullRequest{}, Deletable},
-		{true, "main", []string{}, []PullRequest{}, NotDeletable},
+		{false, "issue1", false, []string{}, []PullRequest{}, Deletable},
+		{true, "main", true, []string{}, []PullRequest{}, NotDeletable},
 	}
 
 	actual, _ := DeleteBranches(branches, s.Conn)
 
 	expected := []Branch{
-		{false, "issue1", []string{}, []PullRequest{}, Deleted},
-		{true, "main", []string{}, []PullRequest{}, NotDeletable},
+		{false, "issue1", false, []string{}, []PullRequest{}, Deleted},
+		{true, "main", true, []string{}, []PullRequest{}, NotDeletable},
 	}
 	assert.Equal(t, expected, actual)
 }
@@ -874,15 +961,15 @@ func Test_DoNotDeleteNotDeletableBranches(t *testing.T) {
 		DeleteBranches(nil, conn.NewConf(&conn.Times{N: 0}))
 
 	branches := []Branch{
-		{false, "issue1", []string{}, []PullRequest{}, NotDeletable},
-		{true, "main", []string{}, []PullRequest{}, NotDeletable},
+		{false, "issue1", false, []string{}, []PullRequest{}, NotDeletable},
+		{true, "main", true, []string{}, []PullRequest{}, NotDeletable},
 	}
 
 	actual, _ := DeleteBranches(branches, s.Conn)
 
 	expected := []Branch{
-		{false, "issue1", []string{}, []PullRequest{}, NotDeletable},
-		{true, "main", []string{}, []PullRequest{}, NotDeletable},
+		{false, "issue1", false, []string{}, []PullRequest{}, NotDeletable},
+		{true, "main", true, []string{}, []PullRequest{}, NotDeletable},
 	}
 	assert.Equal(t, expected, actual)
 }
diff --git a/querygen_test.go b/querygen_test.go
index 758df2a..74cf42d 100644
--- a/querygen_test.go
+++ b/querygen_test.go
@@ -17,42 +17,42 @@ func Test_GetQueryHashes(t *testing.T) {
 			"hash:c1dfd96eea8cc2b62785275bca38ac261256e278",
 		},
 		getQueryHashes([]Branch{
-			{false, "main",
+			{false, "main", false,
 				[]string{},
 				[]PullRequest{}, Unknown,
 			},
-			{true, "issue1",
+			{true, "issue1", false,
 				[]string{
 					"356a192b7913b04c54574d18c28d46e6395428ab",
 				},
 				[]PullRequest{}, Unknown,
 			},
-			{false, "issue2",
+			{false, "issue2", false,
 				[]string{
 					"da4b9237bacccdf19c0760cab7aec4a8359010b0",
 					"08a2aaaadff191eb76974b9b3d8b71f202c0156e",
 				},
 				[]PullRequest{}, Unknown,
 			},
-			{false, "issue3",
+			{false, "issue3", false,
 				[]string{
 					"77de68daecd823babbb58edb1c8e14d7106e83bb",
 				},
 				[]PullRequest{}, Unknown,
 			},
-			{false, "issue4",
+			{false, "issue4", false,
 				[]string{
 					"1b6453892473a467d07372d45eb05abc2031647a",
 				},
 				[]PullRequest{}, Unknown,
 			},
-			{false, "issue5",
+			{false, "issue5", false,
 				[]string{
 					"ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4",
 				},
 				[]PullRequest{}, Unknown,
 			},
-			{false, "issue6",
+			{false, "issue6", false,
 				[]string{
 					"c1dfd96eea8cc2b62785275bca38ac261256e278",
 				},