Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 28 additions & 133 deletions cmd/rm.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package cmd

import (
"bufio"
"errors"
"fmt"
"github.com/barrettj12/jit/common/env"
"github.com/barrettj12/jit/common"
"github.com/barrettj12/jit/common/git"
"github.com/barrettj12/jit/common/path"
"github.com/barrettj12/jit/common/types"
"github.com/spf13/cobra"
"os"
"strings"

"github.com/barrettj12/jit/common"
)

var removeCmd = &cobra.Command{
Expand All @@ -21,142 +17,41 @@ var removeCmd = &cobra.Command{
}

func Remove(cmd *cobra.Command, args []string) error {
// TODO: add --force option
branch, err := common.ReqArg(args, 0, "Which branch would you like to remove?")
if err != nil {
return err
}

split := strings.SplitN(branch, ":", 2)
if len(split) >= 2 {
branch = split[1]
}

// Delete remote tracking branch
// git push -d <remote_name> <branchname>
// TODO: not working when upstream is in origin/source repo

// TODO: this should work if the remote branch doesn't exist (no-op)
//Delete remote tracking branch barrettj12/rm-webster? [y/n]: y
//error: unable to delete 'rm-webster': remote ref does not exist
//error: failed to push some refs to 'https://github.com/barrettj12/interview-questions'
//ERROR: exit status 1
var remoteBranch types.GitHubBranch
var worktree path.Worktree
var localBranch types.LocalBranch

// TODO: need to be able to handle branches with "/" in the name
// $ jit rm imerge/3.3
// Delete remote tracking branch barrettj12/imerge? [y/n]
remote, remoteBranch, err := common.PushLoc(branch)
switch err {
case common.ErrUpstreamNotFound:
// no-op
fmt.Printf("no remote tracking branch found for branch %q\n", branch)

case nil:
ok, err := confirm(fmt.Sprintf("Delete remote tracking branch %s/%s", remote, remoteBranch))
if err != nil {
return err
}
if ok {
err = common.Git("push", "-d", remote, remoteBranch)
if err != nil {
return err
}
}

default: // non-nil error
return err
}
deleteRemoteBranch(remoteBranch)
deleteWorktree(worktree)
deleteLocalBranch(localBranch)
}

// Delete worktree
wktreePath, err := common.WorktreePath(branch)
func deleteRemoteBranch(branch types.GitHubBranch) error {
remote, err := common.GitRemoteFromURL(branch.RepoURL)
if err != nil {
return err
}
// TODO: worktrees can be removed but the worktree still exists in Git.
// Check if it exists in `git worktrees list`, rather than doing a stat.
_, err = os.Stat(wktreePath)
if errors.Is(err, os.ErrNotExist) {
fmt.Printf("no worktree found at %s\n", wktreePath)
} else if err != nil {
return err
} else {
ok, err := confirm(fmt.Sprintf("Delete worktree at %s", wktreePath))
if err != nil {
return err
}
if ok {
err = common.Git("worktree", "remove", wktreePath)
if err != nil {
// Usually the error is "worktree contains modified or untracked files"
// so print these files for the user to see.

// TODO: the err could be
// fatal: <path> is not a working tree
// in which case we need to just skip to the branch removal step

untrackedFiles, _ := common.ExecGit(path.Worktree(wktreePath), "status", "--porcelain", "--ignore-submodules=none")
fmt.Println(untrackedFiles)

force, err := confirm("Worktree deletion failed, try again with --force")
if err != nil {
return err
}
if force {
err = common.Git("worktree", "remove", wktreePath, "--force")
if err != nil {
return err
}
}
}
err = os.RemoveAll(wktreePath)
if err != nil {
return err
}
}
fmt.Printf("WARNING: couldn't find remote matching %q: %v\n", branch.RepoURL.Owner(), err)
fmt.Printf("assuming remote name is %q\n", branch.RepoURL.Owner())
remote = types.RemoteName(branch.RepoURL.Owner())
}

// Fetch merged branch, so that we don't get the error message
// error: The branch ____ is not fully merged.
// TODO: get the remote/branch from gh pr view
_ = common.Fetch("", "")
// TODO: lookup local branch that's tracking the given remote
localBranch := types.LocalBranch(branch.Branch)

// Delete local branch
// git branch -d <branchname>
ok, err := confirm(fmt.Sprintf("Delete local branch %q", branch))
err = git.Push(git.PushArgs{
Branch: localBranch,
Remote: remote,
Delete: true,
})
if err != nil {
return err
}
if ok {
err = common.Git("branch", "-d", branch)
if err != nil {
fmt.Printf("ERROR: %v\n", err)
force, err := confirm("Branch deletion failed, try again with force")
if err != nil {
return err
}
if force {
err = common.Git("branch", "-D", branch)
if err != nil {
return err
}
}
}
return fmt.Errorf("deleting remote branch %q: %w", branch, err)
}

return nil
}

// TODO: move this to common
func confirm(prompt string) (bool, error) {
if env.NonInteractive() {
panic("internal error: common.Prompt called with JIT_NONINTERACTIVE enabled")
}
func deleteWorktree(worktree path.Worktree) {

}

func deleteLocalBranch(branch types.LocalBranch) {

sc := bufio.NewScanner(os.Stdin)
fmt.Printf("%s? [y/n]: ", prompt)
sc.Scan()
if err := sc.Err(); err != nil {
return false, fmt.Errorf("error reading input: %w", err)
}
return sc.Text() == "y", nil
}
155 changes: 155 additions & 0 deletions cmd/rm_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package cmd

import (
"bufio"
"errors"
"fmt"
"github.com/barrettj12/jit/common/env"
"github.com/barrettj12/jit/common/path"
"github.com/spf13/cobra"
"os"
"strings"

"github.com/barrettj12/jit/common"
)

func RemoveV1(cmd *cobra.Command, args []string) error {
// TODO: add --force option
branch, err := common.ReqArg(args, 0, "Which branch would you like to remove?")
if err != nil {
return err
}

split := strings.SplitN(branch, ":", 2)
if len(split) >= 2 {
branch = split[1]
}

// Delete remote tracking branch
// git push -d <remote_name> <branchname>
// TODO: not working when upstream is in origin/source repo

// TODO: this should work if the remote branch doesn't exist (no-op)
//Delete remote tracking branch barrettj12/rm-webster? [y/n]: y
//error: unable to delete 'rm-webster': remote ref does not exist
//error: failed to push some refs to 'https://github.com/barrettj12/interview-questions'
//ERROR: exit status 1

// TODO: need to be able to handle branches with "/" in the name
// $ jit rm imerge/3.3
// Delete remote tracking branch barrettj12/imerge? [y/n]
remote, remoteBranch, err := common.PushLoc(branch)
switch err {
case common.ErrUpstreamNotFound:
// no-op
fmt.Printf("no remote tracking branch found for branch %q\n", branch)

case nil:
ok, err := confirm(fmt.Sprintf("Delete remote tracking branch %s/%s", remote, remoteBranch))
if err != nil {
return err
}
if ok {
err = common.Git("push", "-d", remote, remoteBranch)
if err != nil {
return err
}
}

default: // non-nil error
return err
}

// Delete worktree
wktreePath, err := common.WorktreePath(branch)
if err != nil {
return err
}
// TODO: worktrees can be removed but the worktree still exists in Git.
// Check if it exists in `git worktrees list`, rather than doing a stat.
_, err = os.Stat(wktreePath)
if errors.Is(err, os.ErrNotExist) {
fmt.Printf("no worktree found at %s\n", wktreePath)
} else if err != nil {
return err
} else {
ok, err := confirm(fmt.Sprintf("Delete worktree at %s", wktreePath))
if err != nil {
return err
}
if ok {
err = common.Git("worktree", "remove", wktreePath)
if err != nil {
// Usually the error is "worktree contains modified or untracked files"
// so print these files for the user to see.

// TODO: the err could be
// fatal: <path> is not a working tree
// in which case we need to just skip to the branch removal step

untrackedFiles, _ := common.ExecGit(path.Worktree(wktreePath), "status", "--porcelain", "--ignore-submodules=none")
fmt.Println(untrackedFiles)

force, err := confirm("Worktree deletion failed, try again with --force")
if err != nil {
return err
}
if force {
err = common.Git("worktree", "remove", wktreePath, "--force")
if err != nil {
return err
}
}
}
err = os.RemoveAll(wktreePath)
if err != nil {
return err
}
}
}

// Fetch merged branch, so that we don't get the error message
// error: The branch ____ is not fully merged.
// TODO: get the remote/branch from gh pr view
_ = common.Fetch("", "")

// Delete local branch
// git branch -d <branchname>
ok, err := confirm(fmt.Sprintf("Delete local branch %q", branch))
if err != nil {
return err
}
if ok {
err = common.Git("branch", "-d", branch)
if err != nil {
fmt.Printf("ERROR: %v\n", err)
force, err := confirm("Branch deletion failed, try again with force")
if err != nil {
return err
}
if force {
err = common.Git("branch", "-D", branch)
if err != nil {
return err
}
}
}
}

return nil
}

// TODO: move this to common
func confirm(prompt string) (bool, error) {
if env.NonInteractive() {
panic("internal error: common.Prompt called with JIT_NONINTERACTIVE enabled")
}

sc := bufio.NewScanner(os.Stdin)
fmt.Printf("%s? [y/n]: ", prompt)
sc.Scan()
if err := sc.Err(); err != nil {
return false, fmt.Errorf("error reading input: %w", err)
}
return sc.Text() == "y", nil
}
Loading