Skip to content

Commit

Permalink
added git checkout
Browse files Browse the repository at this point in the history
  • Loading branch information
rsteube committed Sep 11, 2020
1 parent 3f7844a commit 69ba554
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 57 deletions.
150 changes: 114 additions & 36 deletions completers/git_completer/cmd/action/action.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package action

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"

"github.com/rsteube/carapace"
Expand All @@ -27,53 +29,129 @@ func ActionRemotes() carapace.Action {
})
}

// TODO combine with ActionRemoteBranches
func ActionLocalBranches() carapace.Action {
return carapace.ActionCallback(func(args []string) carapace.Action {
if output, err := exec.Command("git", "branch", "--format", "%(refname)").Output(); err != nil {
return carapace.ActionMessage(err.Error())
} else {
refs := make([]string, 0)
for _, line := range strings.Split(string(output), "\n") {
if len(line) > 12 {
refs = append(refs, line[11:])
}
type RefOption struct {
LocalBranches bool
RemoteBranches bool
Commits int
Tags bool
}

var RefOptionDefault = RefOption{
LocalBranches: true,
RemoteBranches: true,
Commits: 100,
Tags: true,
}

type Branch struct {
Name string
Message string
}

func Branches(refOption RefOption) ([]Branch, error) {
args := []string{"branch", "--format", "%(refname)\n%(subject)"}
if refOption.LocalBranches && refOption.RemoteBranches {
args = append(args, "--all")
} else if !refOption.LocalBranches && refOption.RemoteBranches {
args = append(args, "--remote")
} else if !refOption.LocalBranches && !refOption.RemoteBranches {
return []Branch{}, nil
}

if output, err := exec.Command("git", args...).Output(); err != nil {
return nil, err
} else {
lines := strings.Split(string(output), "\n")
branches := make([]Branch, len(lines)/2)
for index, line := range lines[:len(lines)-1] {
if index%2 == 0 {
trimmed := strings.TrimPrefix(line, "refs/heads/")
trimmed = strings.TrimPrefix(trimmed, "refs/remotes/")
branches[index/2] = Branch{trimmed, lines[index+1]}
}
return carapace.ActionValues(refs...)
}
})
return branches, err
}
}

func ActionRemoteBranches() carapace.Action {
return carapace.ActionCallback(func(args []string) carapace.Action {
if output, err := exec.Command("git", "branch", "--remote", "--format", "%(refname)").Output(); err != nil {
return carapace.ActionMessage(err.Error())
} else {
refs := make([]string, 0)
for _, line := range strings.Split(string(output), "\n") {
if len(line) > 14 {
refs = append(refs, line[13:])
}
type Tag struct {
Name string
Message string
}

func Tags(refOption RefOption) ([]Tag, error) {
if !refOption.Tags {
return []Tag{}, nil
}

if output, err := exec.Command("git", "tag", "--format", "%(refname)\n%(subject)").Output(); err != nil {
return nil, err
} else {
lines := strings.Split(string(output), "\n")
tags := make([]Tag, len(lines)/2)
for index, line := range lines[:len(lines)-1] {
if index%2 == 0 {
tags[index/2] = Tag{strings.TrimPrefix(line, "refs/tags/"), lines[index+1]}
}
return carapace.ActionValues(refs...)
}
})
return tags, err
}
}

func ActionCommits() carapace.Action {
return carapace.ActionCallback(func(args []string) carapace.Action {
if output, err := exec.Command("git", "log", "--pretty=tformat:%H %<(64,trunc)%s", "--all", "--max-count", "1000").Output(); err != nil {
return carapace.ActionMessage(err.Error())
} else {
refs := make([]string, 0)
for _, line := range strings.Split(string(output), "\n") {
if len(line) > 14 {
refs = append(refs, line[:40], strings.TrimSpace(line[43:]))
type Commit struct {
Ref string
Message string
}

func Commits(refOption RefOption) ([]Commit, error) {
if refOption.Commits <= 0 {
return []Commit{}, nil
}

if output, err := exec.Command("git", "log", "--pretty=tformat:%h %<(64,trunc)%s", "--all", "--max-count", strconv.Itoa(refOption.Commits)).Output(); err != nil {
return nil, err
} else {
lines := strings.Split(string(output), "\n")
commits := make([]Commit, 0)
for index, line := range lines[:len(lines)-1] {
if len(line) > 10 { // TODO duh?
commits = append(commits, Commit{line[:7], strings.TrimSpace(line[10:])})
if index == 0 {
commits = append(commits, Commit{"HEAD", strings.TrimSpace(line[10:])}) // TOD fix this
} else {
commits = append(commits, Commit{"HEAD~" + fmt.Sprintf("%0"+strconv.Itoa(len(strconv.Itoa(refOption.Commits))-1)+"d", index), strings.TrimSpace(line[10:])}) // TOD fix this
}
}
return carapace.ActionValuesDescribed(refs...)
}
})
return commits, nil
}
}

func ActionRefs(refOption RefOption) carapace.Action {
vals := make([]string, 0)
if branches, err := Branches(refOption); err != nil {
return carapace.ActionMessage(err.Error())
} else {
for _, branch := range branches {
vals = append(vals, branch.Name, branch.Message)
}
}
if commits, err := Commits(refOption); err != nil {
return carapace.ActionMessage(err.Error())
} else {
for _, commit := range commits {
vals = append(vals, commit.Ref, commit.Message)
}
}
if tags, err := Tags(refOption); err != nil {
return carapace.ActionMessage(err.Error())
} else {
for _, tag := range tags {
vals = append(vals, tag.Name, tag.Message)
}
}

return carapace.ActionValuesDescribed(vals...)
}

// TODO multiparts action to complete step by step
Expand Down
23 changes: 11 additions & 12 deletions completers/git_completer/cmd/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ func init() {

carapace.Gen(branchCmd).FlagCompletion(carapace.ActionMap{
"color": carapace.ActionValues("always", "auto", "never"),
"contains": action.ActionCommits(),
"D": action.ActionLocalBranches(),
"delete": action.ActionLocalBranches(),
"merged": action.ActionCommits(),
"no-contains": action.ActionCommits(),
"no-merged": action.ActionCommits(),
"points-at": action.ActionRemoteBranches(),
"set-upstream-to": action.ActionRemoteBranches(),
"contains": action.ActionRefs(action.RefOptionDefault),
"D": action.ActionRefs(action.RefOptionDefault),
"delete": action.ActionRefs(action.RefOption{LocalBranches: true, RemoteBranches: true}),
"merged": action.ActionRefs(action.RefOptionDefault),
"no-contains": action.ActionRefs(action.RefOptionDefault),
"no-merged": action.ActionRefs(action.RefOptionDefault),
"points-at": action.ActionRefs(action.RefOption{RemoteBranches: true, Tags: true}),
"set-upstream-to": action.ActionRefs(action.RefOption{RemoteBranches: true, Tags: true}),
"sort": action.ActionFieldNames(),
})

Expand All @@ -75,7 +75,7 @@ func init() {
branchCmd.Flag("copy").Changed ||
branchCmd.Flag("delete").Changed ||
branchCmd.Flag("edit-description").Changed {
return action.ActionLocalBranches()
return action.ActionRefs(action.RefOption{LocalBranches: true, RemoteBranches: true, Tags: true})
}
case 1:
if branchCmd.Flag("M").Changed ||
Expand All @@ -84,16 +84,15 @@ func init() {
branchCmd.Flag("move").Changed ||
branchCmd.Flag("copy").Changed ||
branchCmd.Flag("delete").Changed {
return action.ActionLocalBranches()
return action.ActionRefs(action.RefOption{LocalBranches: true, RemoteBranches: true, Tags: true})
}
default:
if branchCmd.Flag("D").Changed ||
branchCmd.Flag("delete").Changed {
return action.ActionLocalBranches()
return action.ActionRefs(action.RefOption{LocalBranches: true, RemoteBranches: true, Tags: true})
}
}
return carapace.ActionValues()
}),
)

}
Original file line number Diff line number Diff line change
@@ -1,38 +1,68 @@
package cmd

import (
"os"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace-bin/completers/git_completer/cmd/action"
"github.com/spf13/cobra"
)

var checkoutCmd = &cobra.Command{
Use: "checkout",
Short: "Switch branches or restore working tree files",
Run: func(cmd *cobra.Command, args []string) {
},
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
checkoutCmd.Flags().BoolP("ours", "2", false, "checkout our version for unmerged files")
checkoutCmd.Flags().BoolP("theirs", "3", false, "checkout their version for unmerged files")
checkoutCmd.Flags().StringP("b", "b", "", "create and checkout a new branch")
checkoutCmd.Flags().StringP("B", "B", "", "create/reset and checkout a branch")
carapace.Gen(checkoutCmd).Standalone()

checkoutCmd.Flags().StringS("b", "b", "", "create and checkout a new branch")
checkoutCmd.Flags().StringS("B", "B", "", "create/reset and checkout a branch")
checkoutCmd.Flags().BoolS("l", "l", false, "create reflog for new branch")
checkoutCmd.Flags().String("conflict", "", "conflict style (merge or diff3)")
checkoutCmd.Flags().BoolP("detach", "d", false, "detach HEAD at named commit")
checkoutCmd.Flags().BoolP("force", "f", false, "force checkout (throw away local modifications)")
checkoutCmd.Flags().Bool("guess", false, "second guess 'git checkout <no-such-branch>' (default)")
checkoutCmd.Flags().Bool("ignore-other-worktrees", false, "do not check if another worktree is holding the given ref")
checkoutCmd.Flags().Bool("ignore-skip-worktree-bits", false, "do not limit pathspecs to sparse entries only")
checkoutCmd.Flags().BoolP("l", "l", false, "create reflog for new branch")
checkoutCmd.Flags().BoolP("merge", "m", false, "perform a 3-way merge with the new branch")
checkoutCmd.Flags().String("orphan", "", "new unparented branch")
checkoutCmd.Flags().BoolP("ours", "2", false, "checkout our version for unmerged files")
checkoutCmd.Flags().Bool("overlay", false, "use overlay mode (default)")
checkoutCmd.Flags().Bool("overwrite-ignore", false, "update ignored files (default)")
checkoutCmd.Flags().Bool("pathspec-file-nul", false, "with --pathspec-from-file, pathspec elements are separated with NUL character")
checkoutCmd.Flags().String("pathspec-from-file", "", "read pathspec from file")
checkoutCmd.Flags().BoolP("patch", "p", false, "select hunks interactively")
checkoutCmd.Flags().Bool("pathspec-file-nul", false, "pathspec elements are separated with NUL character")
checkoutCmd.Flags().String("pathspec-from-file", "", "read pathspec from file")
checkoutCmd.Flags().Bool("progress", false, "force progress reporting")
checkoutCmd.Flags().BoolP("quiet", "q", false, "suppress progress reporting")
checkoutCmd.Flags().String("recurse-submodules", "", "control recursive updating of submodules")
checkoutCmd.Flags().BoolP("theirs", "3", false, "checkout their version for unmerged files")
checkoutCmd.Flags().BoolP("track", "t", false, "set upstream info for new branch")
rootCmd.AddCommand(checkoutCmd)

carapace.Gen(checkoutCmd).FlagCompletion(carapace.ActionMap{
"b": action.ActionRefs(action.RefOption{LocalBranches: true}),
"B": action.ActionRefs(action.RefOption{LocalBranches: true}),
"conflict": carapace.ActionValues("merge", "diff3"),
"orphan": action.ActionRefs(action.RefOption{LocalBranches: true, RemoteBranches: true}),
"pathspec-from-file": carapace.ActionFiles(""),
})

carapace.Gen(checkoutCmd).PositionalCompletion(
action.ActionRefs(action.RefOptionDefault),
)

carapace.Gen(checkoutCmd).PositionalAnyCompletion(
carapace.ActionCallback(func(args []string) carapace.Action {
// the first `--` is currently not detected as positional argument,
// so just search the full command line for it and assume it's the divider
for _, arg := range os.Args {
if arg == "--" {
return carapace.ActionFiles("") // TODO files from branch?
}
}
return carapace.ActionValues()
}),
)
}

0 comments on commit 69ba554

Please sign in to comment.