Repository: seachicken/gh-poi (go)

Issue:
Delete the tracking branch as well.
### Description
I would like the tracking branch `(remotes/origin/****)` removed at the same time.

### Steps to reproduce
1. Ensure that the merged branch remains.
    ```sh
    $ git branch -a

      some-topic-branch
    * main
      remotes/origin/some-topic-branch
      remotes/origin/HEAD -> origin/main
      remotes/origin/main
    ```
1. Run gh-poi
    ```sh
    $ gh-poi
    
    ✔ Fetching pull requests...
    ✔ Deleting branches...
    
    Deleted branches
      Some-topic-branch
        └─ #1  https://github.com/Okako-WebWorks/TicketSystem/pull/***
    
    Not deleted branches
    * main
    ```

1. Ensure that the tracking branch remains in place.
    ```sh
    $ git branch -a

    * main
      remotes/origin/some-topic-branch
      remotes/origin/HEAD -> origin/main
      remotes/origin/main
    ```

1. Run prune
    ```
    $ git fetch --prune
    
     - [deleted]         (none)     -> origin/some-topic-branch
    ```

1. Now, all branches was cleaned!
    ```
    $ git branch -a

    * main
      remotes/origin/HEAD -> origin/main
      remotes/origin/main
    ```

Perhaps the prune executed here should be done by gh-poi.
(The above command may not be sufficient to avoid gh-poi deleting tracking branches other than the one you deleted.)

### Additional context
gh version 2.14.3 (2022-07-26)
gh poi  seachicken/gh-poi  v0.5.6

Pull request:
Add remote-tracking branch pruning
- With --dry-run: Do not pruning
- Without --dry-run: `git remote prune` after removing branches

Resolves #67 

Patch:
diff --git a/conn/command.go b/conn/command.go
index be4fe9b..6636435 100644
--- a/conn/command.go
+++ b/conn/command.go
@@ -143,6 +143,13 @@ func (conn *Connection) DeleteBranches(branchNames []string) (string, error) {
 	return run("git", args)
 }
 
+func (conn *Connection) PruneRemoteBranches(remoteName string) (string, error) {
+	args := []string{
+		"remote", "prune", remoteName,
+	}
+	return run("git", args)
+}
+
 func getQueryRepos(repoNames []string) string {
 	var repos strings.Builder
 	for _, name := range repoNames {
diff --git a/main.go b/main.go
index 529ed1f..20ebd04 100644
--- a/main.go
+++ b/main.go
@@ -41,7 +41,13 @@ func runMain(dryRun bool) {
 	sp.Start()
 	var fetchingErr error
 
-	branches, fetchingErr := GetBranches(connection, dryRun)
+	remote, err := GetRemote(connection)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		return
+	}
+
+	branches, fetchingErr := GetBranches(remote, connection, dryRun)
 
 	sp.Stop()
 
@@ -63,6 +69,7 @@ func runMain(dryRun bool) {
 		sp.Restart()
 
 		branches, deletingErr = DeleteBranches(branches, connection)
+		connection.PruneRemoteBranches(remote.Name)
 
 		sp.Stop()
 
diff --git a/poi.go b/poi.go
index 829aa97..1203761 100644
--- a/poi.go
+++ b/poi.go
@@ -75,31 +75,34 @@ const (
 var detachedBranchNameRegex = regexp.MustCompile(`^\(.+\)`)
 var ErrNotFound = errors.New("not found")
 
-func GetBranches(connection Connection, dryRun bool) ([]Branch, error) {
-	var hostname string
-	primaryRepoName := ""
-	if remoteNames, err := connection.GetRemoteNames(); err == nil {
-		remotes := toRemotes(splitLines(remoteNames))
-		if remote, err := getPrimaryRemote(remotes); err == nil {
-			hostname = remote.Hostname
-			if config, err := connection.GetSshConfig(hostname); err == nil {
-				hostname = findHostname(splitLines(config), hostname)
-			}
-			primaryRepoName = remote.RepoName
+func GetRemote(connection Connection) (Remote, error) {
+	remoteNames, err := connection.GetRemoteNames()
+	if err != nil {
+		return Remote{}, err
+	}
+
+	remotes := toRemotes(splitLines(remoteNames))
+	if remote, err := getPrimaryRemote(remotes); err == nil {
+		hostname := remote.Hostname
+		if config, err := connection.GetSshConfig(hostname); err == nil {
+			remote.Hostname = findHostname(splitLines(config), hostname)
 		}
+		return remote, nil
 	} else {
-		return nil, err
+		return Remote{}, err
 	}
+}
 
+func GetBranches(remote Remote, connection Connection, dryRun bool) ([]Branch, error) {
 	var repoNames []string
 	var defaultBranchName string
-	if json, err := connection.GetRepoNames(hostname, primaryRepoName); err == nil {
+	if json, err := connection.GetRepoNames(remote.Hostname, remote.RepoName); err == nil {
 		repoNames, defaultBranchName, _ = getRepo(json)
 	} else {
 		return nil, err
 	}
 
-	err := connection.CheckRepos(hostname, repoNames)
+	err := connection.CheckRepos(remote.Hostname, repoNames)
 	if err != nil {
 		return nil, err
 	}
@@ -122,7 +125,7 @@ func GetBranches(connection Connection, dryRun bool) ([]Branch, error) {
 
 	prs := []PullRequest{}
 	for _, queryHashes := range getQueryHashes(branches) {
-		json, err := connection.GetPullRequests(hostname, repoNames, queryHashes)
+		json, err := connection.GetPullRequests(remote.Hostname, repoNames, queryHashes)
 		if err != nil {
 			return nil, err
 		}


Test patch:
diff --git a/poi_test.go b/poi_test.go
index 370c718..7262062 100644
--- a/poi_test.go
+++ b/poi_test.go
@@ -34,8 +34,9 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithMergedPR(t *testing.T) {
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -87,8 +88,9 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithSquashAndMergedPR(t *testin
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -140,8 +142,9 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithUpstreamSquashAndMergedPR(t
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -193,8 +196,9 @@ func Test_ShouldBeDeletableWhenPRCheckoutBranchesAssociatedWithUpstreamSquashAnd
 			{"branch.fork/main.merge", "forkMain"},
 			{"branch.main.merge", "main"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -247,8 +251,9 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsFalse(t *testing.
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil).
 		CheckoutBranch(nil, conn.NewConf(&conn.Times{N: 1}))
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -301,8 +306,9 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithTheCheckIsTrue(t *testing.T
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil).
 		CheckoutBranch(nil, conn.NewConf(&conn.Times{N: 0}))
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, true)
+	actual, _ := GetBranches(remote, s.Conn, true)
 
 	assert.Equal(t, []Branch{
 		{
@@ -354,8 +360,9 @@ func Test_ShouldBeDeletableWhenBranchIsCheckedOutWithoutADefaultBranch(t *testin
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil).
 		CheckoutBranch(nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -408,8 +415,9 @@ func Test_ShouldNotDeletableWhenBranchHasUncommittedChanges(t *testing.T) {
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil).
 		CheckoutBranch(nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -461,8 +469,9 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithClosedPR(t *testing.T) {
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -514,8 +523,9 @@ func Test_ShouldBeDeletableWhenBranchesAssociatedWithSquashAndMergedAndClosedPRs
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -576,8 +586,9 @@ func Test_ShouldNotDeletableWhenBranchesAssociatedWithNotFullyMergedPR(t *testin
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -630,8 +641,9 @@ func Test_ShouldNotDeletableWhenDefaultBranchAssociatedWithMergedPR(t *testing.T
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -685,8 +697,9 @@ func Test_BranchesAndPRsAreNotAssociatedWhenManyLocalCommitsAreAhead(t *testing.
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -731,8 +744,9 @@ func Test_ShouldBeNoCommitHistoryWhenTheFirstCommitOfATopicBranchIsAssociatedWit
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -772,8 +786,9 @@ func Test_ShouldBeNoCommitHistoryWhenDetachedBranch(t *testing.T) {
 		GetConfig([]conn.ConfigStub{
 			{"branch.main.merge", "main"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	actual, _ := GetBranches(s.Conn, false)
+	actual, _ := GetBranches(remote, s.Conn, false)
 
 	assert.Equal(t, []Branch{
 		{
@@ -798,7 +813,7 @@ func Test_ReturnsAnErrorWhenGetRemoteNamesFails(t *testing.T) {
 	s := conn.Setup(ctrl).
 		GetRemoteNames("origin", errors.New("failed to run external command: git"), nil)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetRemote(s.Conn)
 
 	assert.NotNil(t, err)
 }
@@ -827,8 +842,9 @@ func Test_DoesNotReturnsAnErrorWhenGetSshConfigFails(t *testing.T) {
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.Nil(t, err)
 }
@@ -841,8 +857,9 @@ func Test_ReturnsAnErrorWhenGetRepoNamesFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetSshConfig("github.com", nil, nil).
 		GetRepoNames("origin", errors.New("failed to run external command: gh"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -856,8 +873,9 @@ func Test_ReturnsAnErrorWhenCheckReposFails(t *testing.T) {
 		GetRemoteNames("origin", nil, nil).
 		GetSshConfig("github.com", nil, nil).
 		GetRepoNames("origin", nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -872,8 +890,9 @@ func Test_ReturnsAnErrorWhenGetBranchNamesFails(t *testing.T) {
 		GetSshConfig("github.com", nil, nil).
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", errors.New("failed to run external command: git"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -889,8 +908,9 @@ func Test_ReturnsAnErrorWhenGetMergedBranchNames(t *testing.T) {
 		GetRepoNames("origin", nil, nil).
 		GetBranchNames("@main_issue1", nil, nil).
 		GetMergedBranchNames("@main", errors.New("failed to run external command: git"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -909,8 +929,9 @@ func Test_ReturnsAnErrorWhenGetLogFails(t *testing.T) {
 		GetLog([]conn.LogStub{
 			{"main", "main"}, {"issue1", "issue1"},
 		}, errors.New("failed to run external command: git"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -933,8 +954,9 @@ func Test_ReturnsAnErrorWhenGetAssociatedRefNamesFails(t *testing.T) {
 			{"a97e9630426df5d34ca9ee77ae1159bdfd5ff8f0", "issue1"},
 			{"6ebe3d30d23531af56bd23b5a098d3ccae2a534a", "main_issue1"},
 		}, errors.New("failed to run external command: git"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -958,8 +980,9 @@ func Test_ReturnsAnErrorWhenGetPullRequestsFails(t *testing.T) {
 			{"6ebe3d30d23531af56bd23b5a098d3ccae2a534a", "main_issue1"},
 		}, nil, nil).
 		GetPullRequests("issue1Merged", errors.New("failed to run external command: gh"), nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -988,8 +1011,9 @@ func Test_ReturnsAnErrorWhenGetUncommittedChangesFails(t *testing.T) {
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }
@@ -1019,8 +1043,9 @@ func Test_ReturnsAnErrorWhenCheckoutBranchFails(t *testing.T) {
 			{"branch.main.merge", "main"},
 			{"branch.issue1.merge", "issue1"},
 		}, nil, nil)
+	remote, _ := GetRemote(s.Conn)
 
-	_, err := GetBranches(s.Conn, false)
+	_, err := GetBranches(remote, s.Conn, false)
 
 	assert.NotNil(t, err)
 }