Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix checkouting existing branch. Fixes issue #109 #149

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ If your job is consistently being rate-limited, try incrementally increasing the

## Branch behavior

Passing the `--branch-name` (`-b`) flag is required when running `git-xargs`. If you specify the name of a branch that exists on your remote, its latest changes will be pulled locally prior to your command or script being run. If you specify the name of a new branch that does not yet exist on your remote, it will be created locally and pushed once your changes are committed.
Passing the `--branch-name` (`-b`) flag is required when running `git-xargs`. If you specify the name of a branch that exists on your remote, it is checkout locally prior to your command or script being run. If you specify the name of a new branch that does not yet exist on your remote, it will be created locally and pushed once your changes are committed.

## Default repository branch

Expand Down
102 changes: 52 additions & 50 deletions repository/repo-operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/sirupsen/logrus"

gitConfig "github.com/go-git/go-git/v5/config"
"github.com/google/go-github/v43/github"

"github.com/gruntwork-io/git-xargs/common"
Expand Down Expand Up @@ -162,77 +163,78 @@ func checkoutLocalBranch(config *config.GitXargsConfig, ref *plumbing.Reference,
// BranchName is a global variable that is set in cmd/root.go. It is override-able by the operator via the --branch-name or -b flag. It defaults to "git-xargs"

branchName := plumbing.NewBranchReferenceName(config.BranchName)

logger.WithFields(logrus.Fields{
"Branch Name": branchName,
"Repo": remoteRepository.GetName(),
}).Debug("Created branch")
"Repo": remoteRepository.GetName(),
}).Debug("Fetching remote branches")

// Create a branch specific to the multi repo script runner
co := &git.CheckoutOptions{
Hash: ref.Hash(),
// Fetch remote branches
fetchRemoteBranchesErr := localRepository.Fetch(&git.FetchOptions{
RefSpecs: []gitConfig.RefSpec{"refs/*:refs/*"},
Auth: &http.BasicAuth{
Username: remoteRepository.GetOwner().GetLogin(),
Password: os.Getenv("GITHUB_OAUTH_TOKEN"),
},
})

if fetchRemoteBranchesErr != git.NoErrAlreadyUpToDate && fetchRemoteBranchesErr != nil {
logger.WithFields(logrus.Fields{
"Error": fetchRemoteBranchesErr,
"Repo": remoteRepository.GetName(),
}).Debug("Error fetching remote branches")

// Track the error fetching branches from the remote
config.Stats.TrackSingle(stats.BranchRemoteFetchFailed, remoteRepository)

return branchName, errors.WithStackTrace(fetchRemoteBranchesErr)
}

// Checkout existing branch
checkoutExistingBranchErr := worktree.Checkout(&git.CheckoutOptions{
Branch: branchName,
Create: true,
})

if checkoutExistingBranchErr == nil {
logger.WithFields(logrus.Fields{
"Branch Name": branchName,
"Repo": remoteRepository.GetName(),
}).Debug("Checkout existing branch")

// We have successfully checkout existing branch, exiting
return branchName, nil
}

// Attempt to checkout the new tool-specific branch on which the supplied command will be executed
checkoutErr := worktree.Checkout(co)
// Create new branch
checkoutNewBranchErr := worktree.Checkout(
&git.CheckoutOptions{
Hash: ref.Hash(),
Branch: branchName,
Create: true,
})

if checkoutErr != nil {
if checkoutNewBranchErr != nil {
if config.SkipPullRequests &&
remoteRepository.GetDefaultBranch() == config.BranchName &&
strings.Contains(checkoutErr.Error(), "already exists") {
// User has requested pull requess be skipped, meaning they want their commits pushed on their target branch
strings.Contains(checkoutNewBranchErr.Error(), "already exists") {
// User has requested pull request be skipped, meaning they want their commits pushed on their target branch
// If the target branch is also the repo's default branch and therefore already exists, we don't have an error
} else {
logger.WithFields(logrus.Fields{
"Error": checkoutErr,
"Error": checkoutNewBranchErr,
"Repo": remoteRepository.GetName(),
}).Debug("Error creating new branch")

// Track the error checking out the branch
config.Stats.TrackSingle(stats.BranchCheckoutFailed, remoteRepository)

return branchName, errors.WithStackTrace(checkoutErr)
return branchName, errors.WithStackTrace(checkoutNewBranchErr)
}
}

// Pull latest code from remote branch if it exists to avoid fast-forwarding errors
gitProgressBuffer := bytes.NewBuffer(nil)
po := &git.PullOptions{
RemoteName: "origin",
ReferenceName: branchName,
Auth: &http.BasicAuth{
Username: remoteRepository.GetOwner().GetLogin(),
Password: os.Getenv("GITHUB_OAUTH_TOKEN"),
},
Progress: gitProgressBuffer,
}

logger.WithFields(logrus.Fields{
"Repo": remoteRepository.GetName(),
}).Debug(gitProgressBuffer)

pullErr := worktree.Pull(po)

if pullErr != nil {

if pullErr == plumbing.ErrReferenceNotFound {
// The supplied branch just doesn't exist yet on the remote - this is not a fatal error and will
// allow the new branch to be pushed in pushLocalBranch
config.Stats.TrackSingle(stats.BranchRemoteDidntExistYet, remoteRepository)
return branchName, nil
}

if pullErr == git.NoErrAlreadyUpToDate {
// The local branch is already up to date, which is not a fatal error
return branchName, nil
}

// Track the error pulling the latest from the remote branch
config.Stats.TrackSingle(stats.BranchRemotePullFailed, remoteRepository)

return branchName, errors.WithStackTrace(pullErr)
}
"Branch Name": branchName,
"Repo": remoteRepository.GetName(),
}).Debug("Created new branch")

return branchName, nil
}
Expand Down
6 changes: 3 additions & 3 deletions stats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ const (
CommitsMadeDirectlyToBranch types.Event = "commits-made-directly-to-branch"
// DirectCommitsPushedToRemoteBranch denotes a repo whose changes were pushed to the remote specified branch because the --skip-pull-requests flag was passed
DirectCommitsPushedToRemoteBranch types.Event = "direct-commits-pushed-to-remote"
// BranchRemotePullFailed denotes a repo whose remote branch could not be fetched successfully
BranchRemotePullFailed types.Event = "branch-remote-pull-failed"
// BranchRemoteFetchFailed denotes a repo whose remote branch could not be fetched successfully
BranchRemoteFetchFailed types.Event = "branch-remote-fetch-failed"
// BranchRemoteDidntExistYet denotes a repo whose specified branch didn't exist remotely yet and so was just created locally to begin with
BranchRemoteDidntExistYet types.Event = "branch-remote-didnt-exist-yet"
// RepoFlagSuppliedRepoMalformed denotes a repo passed via the --repo flag that was malformed (perhaps missing it's Github org prefix) and therefore unprocessable
Expand Down Expand Up @@ -104,7 +104,7 @@ var allEvents = []types.AnnotatedEvent{
{Event: PullRequestAlreadyExists, Description: "Repos where opening a pull request was skipped because a pull request was already open"},
{Event: CommitsMadeDirectlyToBranch, Description: "Repos whose local changes were committed directly to the specified branch because --skip-pull-requests was passed"},
{Event: DirectCommitsPushedToRemoteBranch, Description: "Repos whose changes were pushed directly to the remote branch because --skip-pull-requests was passed"},
{Event: BranchRemotePullFailed, Description: "Repos whose remote branches could not be successfully pulled"},
{Event: BranchRemoteFetchFailed, Description: "Repos whose remote branches could not be successfully fetched"},
{Event: BranchRemoteDidntExistYet, Description: "Repos whose specified branches did not exist on the remote, and so were first created locally"},
{Event: RepoFlagSuppliedRepoMalformed, Description: "Repos passed via the --repo flag that were malformed (missing their Github org prefix?) and therefore unprocessable"},
{Event: RepoDoesntSupportDraftPullRequestsErr, Description: "Repos that do not support Draft PRs (--draft flag was passed)"},
Expand Down