From aa88b6c9895eb2b8178ec500388b14bb31cdc58a Mon Sep 17 00:00:00 2001 From: niboshi Date: Wed, 9 Dec 2020 15:26:42 +0000 Subject: [PATCH] push: Resolve from commit --- cmd/push.go | 40 +++++++++++++++++++++++++++++++++++++++- pkg/ghost/git/repo.go | 26 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/cmd/push.go b/cmd/push.go index 32ceab3..0f5542c 100644 --- a/cmd/push.go +++ b/cmd/push.go @@ -17,8 +17,10 @@ package cmd import ( "fmt" "os" + "strings" "github.com/pfnet-research/git-ghost/pkg/ghost" + "github.com/pfnet-research/git-ghost/pkg/ghost/git" "github.com/pfnet-research/git-ghost/pkg/ghost/types" "github.com/pfnet-research/git-ghost/pkg/util/errors" @@ -43,7 +45,7 @@ func NewPushCommand() *cobra.Command { Short: "push commits(hash1...hash2), diff(hash...current state) to your ghost repo", Long: "push commits or diff or all to your ghost repo. If you didn't specify any subcommand, this commands works as an alias for 'push diff' command.", Args: cobra.RangeArgs(0, 1), - Run: runPushDiffCommand(&flags), + Run: runPushCommitsCommand(&flags), } command.AddCommand(&cobra.Command{ Use: "commits [from-hash] [to-hash(default=HEAD)]", @@ -73,6 +75,32 @@ func NewPushCommand() *cobra.Command { return command } +func getFirstRemoteAncestorCommit(commit string) (string, errors.GitGhostError) { + dir := globalOpts.srcDir + for { + branchNames, err := git.GetRemoteBranchesContainingCommit(dir, commit) + if err != nil { + return "", err + } + var originBranchNames []string + for _, branchName := range branchNames { + if strings.HasPrefix(branchName, fmt.Sprintf("refs/remotes/%s/", git.ORIGIN)) { + originBranchNames = append(originBranchNames, branchName) + } + } + if len(originBranchNames) > 0 { + // This commit is the first ancestor commit in the origin remote. + break + } + parentCommit, err := git.GetParentCommit(dir, commit) + if err != nil { + return "", err + } + commit = parentCommit + } + return commit, nil +} + type pushCommitsArg struct { commitsFrom string commitsTo string @@ -111,6 +139,16 @@ func (arg pushCommitsArg) validate() errors.GitGhostError { func runPushCommitsCommand(flags *pushFlags) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { pushArg := newPushCommitsArg(args) + + // If commitsFrom is not given, find the first ancestor commit that is included in the origin remote. + if pushArg.commitsFrom == "" { + commitsFrom, err := getFirstRemoteAncestorCommit(pushArg.commitsTo) + if err != nil { + errors.LogErrorWithStack(err) + os.Exit(1) + } + pushArg.commitsFrom = commitsFrom + } if err := pushArg.validate(); err != nil { errors.LogErrorWithStack(err) os.Exit(1) diff --git a/pkg/ghost/git/repo.go b/pkg/ghost/git/repo.go index fff6a8b..ed14e44 100644 --- a/pkg/ghost/git/repo.go +++ b/pkg/ghost/git/repo.go @@ -15,6 +15,7 @@ package git import ( + "bytes" "fmt" "os/exec" "strings" @@ -146,3 +147,28 @@ func ResetHardToBranch(dir, branch string) errors.GitGhostError { exec.Command("git", "-C", dir, "reset", "--hard", branch), ) } + +// GetParentCommit returns the parent commit of the given commit +func GetParentCommit(dir, commit string) (string, errors.GitGhostError) { + resultBytes, err := util.JustOutputCmd(exec.Command("git", "-C", dir, "rev-parse", fmt.Sprintf("%s^", commit))) + if err != nil { + return "", errors.WithStack(gherrors.WithMessage(err, "failed to get the parent commit of a commit")) + } + return strings.TrimSuffix(string(resultBytes), "\n"), nil +} + +// GetRemoteBranchesContainingCommit returns a slice of remote branch names each of which contain the given commit +func GetRemoteBranchesContainingCommit(dir, commit string) ([]string, errors.GitGhostError) { + resultBytes, err := util.JustOutputCmd(exec.Command("git", "-C", dir, "branch", "--format", "%(refname)", "--remotes", "--contains", commit)) + if err != nil { + return nil, errors.WithStack(gherrors.WithMessage(err, "failed to get remote branches containing a commit")) + } + var branchNames []string + for _, line := range bytes.Split(resultBytes, []byte("\n")) { + branchName := string(line) + if len(branchName) > 0 { + branchNames = append(branchNames, branchName) + } + } + return branchNames, nil +}