gitcmd

package
v0.0.0-...-9c55465 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2024 License: MIT Imports: 37 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var BatchCmd = &gcli.Command{
	Name:    "batch",
	Aliases: []string{"bat"},
	Desc:    "provide some useful dev tools commands",
	Subs: []*gcli.Command{
		BatchRunCmd,
		BatchPullCmd,
	},
	Config: func(c *gcli.Command) {

	},
}

BatchCmd command

View Source
var BatchPullCmd = &gcli.Command{
	Name:    "pull",
	Desc:    "batch pull multi git directory by `git pull`",
	Aliases: []string{"pul", "pl"},
	Config: func(c *gcli.Command) {
		c.
			AddArg("baseDir", "base directory for run batch pull, default is work dir").
			WithValue("./")

		c.VarOpt(&btpOpts.dirs, "dirs", "", "limit update the given dir names")
	},
	Func: func(c *gcli.Command, args []string) error {
		baseDir := c.Arg("baseDir").String()
		absDir, err := filepath.Abs(baseDir)
		if err != nil {
			return err
		}

		dirNames := btpOpts.dirs.Split(",")

		dump.P(dirNames, baseDir, absDir)

		return nil
	},
}
View Source
var BatchRunCmd = &gcli.Command{
	Name:    "run",
	Desc:    "batch run git command in the given dirs",
	Aliases: []string{"exec"},
	Config: func(c *gcli.Command) {
		btrOpts.BindCommonFlags(c)

		c.AddArg("dirs", "run command in the given dirs, if empty, run on all subdir")
	},
	Func: func(c *gcli.Command, args []string) error {

		return nil
	},
}
View Source
var BranchCreateCmd = &gcli.Command{
	Name: "new",
	Desc: "create and checkout new branch for development",
	Help: `Workflow:
 1. git checkout DEFAULT_BRANCH
 2. git pull -np SOURCE_REMOTE DEFAULT_BRANCH
 3. git checkout -b NEW_BRANCH
 4. git push -u DEFAULT_REMOTE NEW_BRANCH
 5. git push SOURCE_REMOTE NEW_BRANCH

Note:
 - if branch name is "fix_" or contains "{ymd}", will auto add current date string.
   eg: fix_ => fix_210101
`,
	Aliases: []string{"n", "create"},
	Config: func(c *gcli.Command) {
		bcOpts.BindCommonFlags(c)
		c.BoolOpt2(&bcOpts.notToSrc, "not-to-src, nts", "dont push new branch to the source remote")

		c.AddArg("branch", "the new branch name, allow vars: {ymd}", true)
	},
	Func: func(c *gcli.Command, args []string) error {
		cfg := apputil.GitCfgByCmdID(c)

		rp := cfg.LoadRepo(upOpts.Workdir)
		if err := rp.Check(); err != nil {
			return err
		}

		defRemote := rp.DefaultRemote
		srcRemote := rp.SourceRemote
		defBranch := rp.DefaultBranch
		newBranch := c.Arg("branch").String()
		ymdString := timex.Now().DateFormat("ymd")
		if newBranch == "fix_" {
			newBranch = "fix_" + ymdString
		} else if strings.Contains(newBranch, "{ymd}") {
			newBranch = strings.Replace(newBranch, "{ymd}", ymdString, -1)
		}

		rr := cmdutil.NewRunner(func(rr *cmdutil.Runner) {
			rr.DryRun = acpOpts.DryRun
			rr.Confirm = acpOpts.Confirm
			rr.OutToStd = true
		})

		if rp.HasLocalBranch(newBranch) {
			c.Infof("TIP: local ")
			rr.GitCmd("checkout", newBranch).GitCmd("pull", "-np")
			return rr.Run()
		}

		colorp.Infoln("Fetch remotes and check branch exists")
		rr.GitCmd("fetch", "--all", "-np")
		if err := rr.RunReset(); err != nil {
			return err
		}

		rr.Reset()

		if rp.HasOriginBranch(newBranch) {
			colorp.Warnf("the branch %q already exists in remote %q\n", newBranch, defRemote)
			return rp.QuickRun("checkout", newBranch)
		}

		if rp.HasSourceBranch(newBranch) {
			colorp.Warnf("the branch %q already exists in remote %q\n", newBranch, srcRemote)
			rr.GitCmd("checkout", newBranch).GitCmd("push", "-u", defRemote, newBranch)
			return rr.Run()
		}

		colorp.Infoln("Do checkout new branch and push to remote")
		curBranch := rp.CurBranchName()
		if defBranch != curBranch {
			rr.GitCmd("checkout", defBranch)
		}

		rr.GitCmd("pull", srcRemote, defBranch)
		rr.GitCmd("checkout", "-b", newBranch)
		rr.GitCmd("push", "-u", defRemote, newBranch)

		if !bcOpts.notToSrc {
			rr.GitCmd("push", srcRemote, newBranch)
		}
		return rr.Run()
	},
}

BranchCreateCmd instance

View Source
var BranchDeleteCmd = &gcli.Command{
	Name:    "delete",
	Desc:    "delete branches",
	Aliases: []string{"del", "rm"},
	Examples: `
# Delete local branches
{$fullCmd} fix_1 fix_2 fea_1
{$fullCmd} "fea*" # use glob match
{$fullCmd} "prefix:fea" # use prefix match
{$fullCmd} "suffix:_dev" # use suffix match
{$fullCmd} "regex:fix_\d+" # use regex match

# Delete remote branches
{$fullCmd} -r origin fix_* # use glob match
`,
	Config: func(c *gcli.Command) {
		bdOpts.BindCommonFlags(c)
		c.MustFromStruct(&bdOpts)
		c.AddArg("branches", "the branch names or match pattern(can with match mode)", true, true)
	},
	Func: func(c *gcli.Command, args []string) error {
		rp := app.Gitx().LoadRepo(blOpts.Workdir)
		if blOpts.Fetch {
			if err := rp.FetchAll("-np"); err != nil {
				return err
			}
		}

		matcher := brinfo.NewMulti()
		patterns := c.Arg("branches").Strings()

		var branches []string
		for _, pattern := range patterns {
			if gitutil.IsBranchName(pattern) {
				branches = append(branches, pattern)
				continue
			}

			matcher.Add(brinfo.NewMatcher(pattern))
		}

		bis := rp.BranchInfos()
		opt := &gitw.SearchOpt{Flag: gitw.BrSearchLocal, Remote: bdOpts.Remote}
		if bdOpts.Remote != "" {
			opt.Flag = gitw.BrSearchRemote
		}

		brs := bis.SearchV2(matcher, opt)
		num := len(brs)

		rp.SetDryRun(blOpts.DryRun)
		colorp.Cyanf("Will delete %d branches, Glob match number %d\n", num+len(branches), num)

		if blOpts.Confirm && !cliutil.Confirm("Do you want to continue?") {
			colorp.Cyanln("Canceled")
			return nil
		}

		for _, info := range brs {

			colorp.Warnf("Do delete branch: %s\n", info.Name)
			if err := rp.BranchDelete(info.Short, info.Remote); err != nil {
				return err
			}
		}
		return nil
	},
}

BranchDeleteCmd quickly delete branches

View Source
var BranchListCmd = &gcli.Command{
	Name:    "list",
	Desc:    "list or search branches on local or remote",
	Aliases: []string{"search", "ls"},
	Examples: `
# List branches by glob pattern
{$fullCmd} -m "fea*"
{$fullCmd} -m "glob:fix_2[1|2]*"
# List branches by prefix pattern
{$fullCmd} -m "prefix:fea-"

# List branches by regex pattern
{$fullCmd} -m "regex:fea_\d+"
# match like fix_23_04
{$fullCmd} -m "regex:f[a-z]*_[0-9]+_\d+"

# Find and delete remote branches
{$fullCmd} -r origin -m fix_* --delete
# or
{$fullCmd} -r origin -m fix_* -x "git push {remote} --delete {branch}"
`,
	Config: func(c *gcli.Command) {
		blOpts.BindCommonFlags(c)
		c.MustFromStruct(&blOpts, gflag.TagRuleNamed)
		c.AddArg("match", "the branch name match pattern, same as --match|-m|-p").WithAfterFn(func(arg *gcli.CliArg) error {
			blOpts.Match = arg.String()
			return nil
		})
	},
	Func: func(c *gcli.Command, args []string) error {
		rp := app.Gitx().LoadRepo(blOpts.Workdir)

		if blOpts.Fetch {
			if err := rp.FetchAll("-np"); err != nil {
				return err
			}
		}

		colorp.Infoln("Load repo branches ...")
		bis := rp.BranchInfos()

		if blOpts.Match == "" {
			tle := "Local"
			var brs []*gitw.BranchInfo

			if blOpts.All {
				tle = "Local+Remotes"
				brs = bis.All()
			} else if blOpts.Remote != "" {
				tle = blOpts.Remote
				brs = bis.Remotes(blOpts.Remote)
			} else {
				brs = bis.Locales()
			}

			colorp.Cyanf("Branches on %q(total: %d)\n", tle, len(brs))
			for i, info := range brs {
				colorp.Infof(" %-24s %s\n", info.Short, info.HashMsg)
				if blOpts.Limit > 0 && i+1 >= blOpts.Limit {
					break
				}
			}
			return nil
		}

		tle := "Local"
		if blOpts.All {
			tle = "Local+Remotes"
		} else if blOpts.Remote != "" {
			tle = blOpts.Remote
		}

		matcher := brinfo.NewMatcher(blOpts.Match)

		opt := &gitw.SearchOpt{Flag: gitw.BrSearchLocal, Remote: blOpts.Remote, Limit: blOpts.Limit}
		brs := bis.SearchV2(matcher, opt)
		colorp.Cyanf("Branches on %q(found: %d, %s)\n", tle, len(brs), matcher.String())

		if blOpts.Confirm && !cliutil.Confirm("Do you want to continue?") {
			colorp.Cyanln("Canceled")
			return nil
		}

		spl := textutil.NewVarReplacer("{,}")
		rp.SetDryRun(blOpts.DryRun)

		for _, info := range brs {
			if blOpts.Delete {

				colorp.Warnf("Do delete branch: %s\n", info.Name)
				if err := rp.BranchDelete(info.Short, info.Remote); err != nil {
					return err
				}
			} else if blOpts.Exec != "" {
				vs := map[string]string{
					"branch": info.Short,
					"hash":   info.Hash,
					"remote": info.Remote,
				}

				execCmd := cmdr.NewCmdline(spl.RenderSimple(blOpts.Exec, vs)).
					WithWorkDir(rp.Dir()).
					WithDryRun(blOpts.DryRun).
					PrintCmdline()

				if err := execCmd.Run(); err != nil {
					return err
				}
			} else {
				colorp.Infof(" %-24s %s\n", info.Short, info.HashMsg)
			}
		}

		if blOpts.Update {
			if err := rp.FetchAll("-np"); err != nil {
				return err
			}
		}
		return nil
	},
}

BranchListCmd instance

View Source
var BranchSetupCmd = &gcli.Command{
	Name: "setup",
	Desc: "setup a new checkout branch on fork develop mode",
	Help: `Workflow:
git fetch DEFAULT_REMOTE

if DEFAULT_REMOTE/BRANCH exist:
	git branch --set-upstream-to=DEFAULT_REMOTE/BRANCH
else:
	git push --set-upstream DEFAULT_REMOTE BRANCH

git push SOURCE_REMOTE BRANCH
`,
	Aliases: []string{"init"},
	Config: func(c *gcli.Command) {
		bsOpts.BindCommonFlags(c)
		c.BoolOpt2(&bsOpts.notToSrc, "not-to-src, nts", "dont push branch to the source remote")
	},
	Func: func(c *gcli.Command, args []string) (err error) {
		cfg := apputil.GitCfgByCmdID(c)
		rp := cfg.LoadRepo(bsOpts.Workdir)
		if err := rp.Check(); err != nil {
			return err
		}

		if err := rp.FetchOrigin(); err != nil {
			return err
		}

		brName := rp.CurBranchName()
		if rp.HasOriginBranch(brName) {
			err = rp.Cmd("branch").Argf("--set-upstream-to=%s/%s", rp.DefaultRemote, brName).Run()
		} else {
			err = rp.Cmd("push", "-u", rp.DefaultRemote, brName).Run()
		}

		return basefn.CallOn(!bsOpts.notToSrc, func() error {
			return rp.Cmd("push", rp.SourceRemote, brName).Run()
		})
	},
}

BranchSetupCmd instance

View Source
var (

	// ChangelogCmd instance
	ChangelogCmd = &gcli.Command{
		Name:    "chlog",
		Desc:    "Generate changelog message for git repository",
		Aliases: []string{"cl", "clog", "changelog"},
		Examples: `
  {$binWithCmd} last head
  {$binWithCmd} last head --style gh-release --no-merges
  {$binWithPath} v2.0.9 v2.0.10 --no-merges --style gh-release --exclude "cs-fixer,format codes"
`,
		Config: func(c *gcli.Command) {
			c.AddArg("oldVersion", `The old version. eg: v1.0.2, 349238b
- keywords 'last/latest' will auto use latest tag
- keywords 'prev/previous' will auto use previous tag`).
				WithFn(func(arg *gcli.Argument) {
					arg.Required = true
				})

			c.AddArg("newVersion", `The new version. eg: v1.2.2, 66c0df1
- keywords 'head' will auto use Head commit`).
				WithFn(func(arg *gcli.Argument) {
					arg.Required = true
				})

			c.VarOpt(&chlogOpts.limit, "limit", "", "limit update the given dir names")
			c.StrOpt2(&chlogOpts.dstFile, "file", "Export changelog message to the file, default dump to stdout")
			c.StrOpt2(&chlogOpts.repoUrl, "repo-url", `
The git repo URL address. eg: https://github.com/inhere/kite
 default will auto use current git origin remote url
`)
			c.StrOpt(&chlogOpts.style, "style", "s", "default", `
The style for generate for changelog.
 allow: markdown(md), simple, gh-release(ghr)
`)
			c.BoolOpt2(&chlogOpts.fetchTags, "fetch-tags", "Update repo tags list by 'git fetch --tags'")
			c.BoolOpt2(&chlogOpts.noMerges, "no-merges", "dont contains merge request logs")
			c.BoolOpt2(&chlogOpts.unShallow, "unshallow", "Convert to a complete warehouse, useful on GitHub Action.")
		},
		Func: func(c *gcli.Command, args []string) error {

			absDir, err := filepath.Abs(c.WorkDir())
			if err != nil {
				return err
			}

			repo := gitw.NewRepo(absDir)
			colorp.Infoln("chlog options:")
			dump.NoLoc(repo.DefaultRemoteInfo())

			cl := chlog.New()
			cl.Formatter = &chlog.MarkdownFormatter{
				RepoURL: "https://github.com/gookit/gitw",
			}

			cl.WithConfigFn(func(c *chlog.Config) {
				c.GroupPrefix = "\n### "
				c.GroupSuffix = "\n"
			})

			chlogOpts.sha1 = c.Arg("oldVersion").String()
			chlogOpts.sha2 = c.Arg("newVersion").String()
			colorp.Infoln("chlog options:")
			dump.NoLoc(chlogOpts)

			cl.FetchGitLog(chlogOpts.sha1, chlogOpts.sha2, "--no-merges")
			if cl.LogIsEmpty() {
				colorp.Infoln("Not found change log in two hash version")
				return nil
			}

			if err = cl.Generate(); err != nil {
				return err
			}

			dump.P(cl.Changelog())
			return nil
		},
	}
)
View Source
var CreatePRLink = &gcli.Command{
	Name:    "pr",
	Desc:    "create pull request link for current project",
	Aliases: []string{"pr-link"},
}
View Source
var GitCommands = &gcli.Command{
	Name:    "git",
	Desc:    "tools for quick use `git` and more extra commands",
	Aliases: []string{"g", "gitx"},
	Subs: []*gcli.Command{
		RepoInfoCmd,

		RemoteInfoCmd,
		NewCloneCmd(configProvider),
		NewAddCommitPush(),
		NewAddCommitCmd(),
		NewUpdateCmd(),
		NewUpdatePushCmd(),
		NewOpenRemoteCmd(configProvider),
		NewInitFlowCmd(),
		CreatePRLink,
		NewCheckoutCmd(),
		ShowLogCmd,
		ChangelogCmd,
		TagCmd,
		BatchCmd,
		NewBranchCmd(),
		NewGitEmojisCmd(),
	},
	Config: func(c *gcli.Command) {
		GitOpts.BindCommonFlags(c)
		GitOpts.BindChdirFlags(c)

		c.On(events.OnCmdSubNotFound, SubCmdNotFound)
		c.On(events.OnCmdRunBefore, func(ctx *gcli.HookCtx) bool {
			c.Infoln("[kite.GIT] Workdir:", c.WorkDir())
			return false
		})
	},
}

GitCommands commands for use git

View Source
var GitOpts = struct {
	AutoChDir
	cmdbiz.CommonOpts
}{}

GitOpts object

View Source
var RemoteInfoCmd = &gcli.Command{
	Name:    "remote",
	Aliases: []string{"rmt"},
	Desc:    "git remote command",
	Func: func(c *gcli.Command, args []string) error {
		err := gitw.New("remote", "-v").Run()
		if err != nil {
			return err
		}
		return nil
	},
}

RemoteInfoCmd instance

View Source
var RepoInfoCmd = &gcli.Command{
	Name: "info",

	Desc: "show some info for the git repository",
	Config: func(c *gcli.Command) {
		riOpts.BindCommonFlags(c)
	},
	Func: func(c *gcli.Command, args []string) error {
		rp := gitw.NewRepo(riOpts.Workdir)

		show.AList("Information", rp.Info())
		return nil
	},
}

RepoInfoCmd instance

View Source
var (
	ShowLogCmd = &gcli.Command{
		Name: "log",
		Desc: "display recently git commits information by `git log`",

		Config: func(c *gcli.Command) {
			c.UseSimpleRule()
			goutil.PanicErr(c.FromStruct(&logOpts))

			c.StrOpt(&logOpts.Format, "format", "", "",
				"The git log option '--pretty' value.\n"+
					"can be one of oneline, short, medium, full, fuller, reference, email, raw, format:string and tformat:string.",
			)

			c.AddArg("maxCommit", "Max display how many commits")
		},
		Func: func(c *gcli.Command, args []string) error {
			maxNum := c.Arg("maxCommit").Int()
			if maxNum < 1 {
				maxNum = logOpts.MaxCommit
			}

			gitLog := gitw.New("log", "--graph")
			gitLog.OnBeforeExec(gitw.PrintCmdline)

			gitLog.Argf("-%d", maxNum)
			gitLog.ArgIf("--color", !logOpts.NoColor)
			gitLog.ArgIf("--no-merges", logOpts.NoMerges)
			gitLog.ArgIf("--abbrev-commit", logOpts.Abbrev)
			gitLog.Add(`--pretty=format:%Cred%h%Creset:%C(ul yellow)%d%Creset %s (%Cgreen%cr%Creset, %C(bold blue)%an%Creset)`)

			return gitLog.Run()
		},
	}
)
View Source
var TagCmd = &gcli.Command{
	Name: "tag",
	Desc: "extra git tag commands",
	Subs: []*gcli.Command{
		TagListCmd,
		TagCreateCmd,
		TagDeleteCmd,
	},
}

TagCmd instance

View Source
var TagCreateCmd = &gcli.Command{
	Name:    "create",
	Aliases: []string{"new"},
	Desc:    "create new tag by `git tag`",
	Help: `
# Examples:
  {$fullCmd} --next
  {$fullCmd} -v v2.0.1
`,
	Config: func(c *gcli.Command) {
		c.MustFromStruct(&tcOpts, gflag.TagRuleSimple)
	},
	Func: func(c *gcli.Command, args []string) error {
		lp := gitw.NewRepo(GitOpts.Workdir).
			PrintCmdOnExec().
			SetDryRun(GitOpts.DryRun)

		err := lp.Cmd("pull", "-np").Run()
		if err != nil {
			return err
		}

		err = lp.Cmd("fetch", "--tags").Run()
		if err != nil {
			return err
		}

		lastVer := lp.LargestTag()
		nextVer := tcOpts.Version
		if len(nextVer) == 0 {
			nextVer = gitutil.NextVersion(lastVer)
		} else {
			var ok bool
			nextVer, ok = gitutil.FormatVersion(nextVer)
			if !ok {
				return c.NewErrf("invalid version: %s", nextVer)
			}
		}

		nextVer = "v" + nextVer
		message := tcOpts.Message
		if len(message) == 0 {
			message = ":bookmark: release the " + nextVer
		}

		show.AList("create tag", map[string]any{
			"Hash ID":     tcOpts.Hash,
			"Prev tag":    lastVer,
			"New tag":     nextVer,
			"Tag Message": message,
			"Dry Run":     GitOpts.DryRun,
		})

		if interact.Unconfirmed("Ensure create and push new tag?", true) {
			colorp.Infoln("Quit, Bye!")
			return nil
		}

		err = lp.Cmd("tag", "-a", nextVer, "-m", message).Run()
		if err != nil {
			return err
		}

		err = lp.Cmd("push", "origin", nextVer).Run()
		if err != nil {
			return err
		}

		colorp.Successf("Successful create tag: %s\n", nextVer)
		return nil
	},
}

TagCreateCmd instance

View Source
var TagDeleteCmd = &gcli.Command{
	Name:    "delete",
	Aliases: []string{"del", "rm", "remove"},
	Desc:    "delete exists tags by `git tag`",
	Func: func(c *gcli.Command, args []string) error {
		return errors.New("TODO")
	},
}

TagDeleteCmd instance

View Source
var TagListCmd = &gcli.Command{
	Name:    "list",
	Aliases: []string{"ls"},
	Desc:    "list tags for the git repository",
	Func: func(c *gcli.Command, args []string) error {
		return errors.New("TODO")
	},
}

TagListCmd instance

Functions

func NewAddCommitCmd

func NewAddCommitCmd() *gcli.Command

NewAddCommitCmd instance

func NewAddCommitPush

func NewAddCommitPush() *gcli.Command

NewAddCommitPush command

func NewBranchCmd

func NewBranchCmd() *gcli.Command

NewBranchCmd instance

func NewCheckoutCmd

func NewCheckoutCmd() *gcli.Command

NewCheckoutCmd instance

func NewCloneCmd

func NewCloneCmd(cfgGetter gitx.ConfigProviderFn) *gcli.Command

NewCloneCmd instance

func NewGitEmojisCmd

func NewGitEmojisCmd() *gcli.Command

NewGitEmojisCmd instance

func NewInitFlowCmd

func NewInitFlowCmd() *gcli.Command

NewInitFlowCmd instance

func NewOpenRemoteCmd

func NewOpenRemoteCmd(cfgGetter gitx.ConfigProviderFn) *gcli.Command

NewOpenRemoteCmd instance

func NewUpdateCmd

func NewUpdateCmd() *gcli.Command

NewUpdateCmd instance

func NewUpdatePushCmd

func NewUpdatePushCmd() *gcli.Command

NewUpdatePushCmd instance

func RedirectToGitx

func RedirectToGitx(ctx *gcli.HookCtx) (stop bool)

RedirectToGitx handle

func SubCmdNotFound

func SubCmdNotFound(ctx *gcli.HookCtx) (stop bool)

SubCmdNotFound handle

Types

type AutoChDir

type AutoChDir struct {
	// AutoGit auto find .git dir in parent.
	AutoGit bool
}

AutoChDir auto change dir

func (*AutoChDir) BindChdirFlags

func (a *AutoChDir) BindChdirFlags(c *gcli.Command)

BindChdirFlags for auto change dir

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL