Skip to content

Commit e567ce0

Browse files
authored
Merge pull request cli#4102 from cli/sync-no-checkout
Repo sync optimizations
2 parents c39dd1e + f4bded3 commit e567ce0

File tree

4 files changed

+100
-120
lines changed

4 files changed

+100
-120
lines changed

pkg/cmd/repo/sync/git.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99

1010
type gitClient interface {
1111
BranchRemote(string) (string, error)
12-
CheckoutLocal(string) error
13-
CheckoutRemote(string, string) error
1412
CurrentBranch() (string, error)
13+
UpdateBranch(string, string) error
14+
CreateBranch(string, string, string) error
1515
Fetch(string, string) error
1616
HasLocalBranch(string) bool
1717
IsAncestor(string, string) (bool, error)
@@ -36,18 +36,23 @@ func (g *gitExecuter) BranchRemote(branch string) (string, error) {
3636
return parts[0], nil
3737
}
3838

39-
func (g *gitExecuter) CheckoutLocal(branch string) error {
40-
args := []string{"checkout", branch}
41-
cmd, err := git.GitCommand(args...)
39+
func (g *gitExecuter) UpdateBranch(branch, ref string) error {
40+
cmd, err := git.GitCommand("update-ref", fmt.Sprintf("refs/heads/%s", branch), ref)
4241
if err != nil {
4342
return err
4443
}
4544
return cmd.Run()
4645
}
4746

48-
func (g *gitExecuter) CheckoutRemote(remote, branch string) error {
49-
args := []string{"checkout", "--track", fmt.Sprintf("%s/%s", remote, branch)}
50-
cmd, err := git.GitCommand(args...)
47+
func (g *gitExecuter) CreateBranch(branch, ref, upstream string) error {
48+
cmd, err := git.GitCommand("branch", branch, ref)
49+
if err != nil {
50+
return err
51+
}
52+
if err := cmd.Run(); err != nil {
53+
return err
54+
}
55+
cmd, err = git.GitCommand("branch", "--set-upstream-to", upstream, branch)
5156
if err != nil {
5257
return err
5358
}
@@ -97,7 +102,7 @@ func (g *gitExecuter) IsDirty() (bool, error) {
97102
}
98103

99104
func (g *gitExecuter) MergeFastForward(ref string) error {
100-
args := []string{"merge", "--ff-only", ref}
105+
args := []string{"merge", "--ff-only", "--quiet", ref}
101106
cmd, err := git.GitCommand(args...)
102107
if err != nil {
103108
return err

pkg/cmd/repo/sync/mocks.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ func (g *mockGitClient) BranchRemote(a string) (string, error) {
1313
return args.String(0), args.Error(1)
1414
}
1515

16-
func (g *mockGitClient) CheckoutLocal(a string) error {
17-
args := g.Called(a)
16+
func (g *mockGitClient) UpdateBranch(b, r string) error {
17+
args := g.Called(b, r)
1818
return args.Error(0)
1919
}
2020

21-
func (g *mockGitClient) CheckoutRemote(a, b string) error {
22-
args := g.Called(a, b)
21+
func (g *mockGitClient) CreateBranch(b, r, u string) error {
22+
args := g.Called(b, r, u)
2323
return args.Error(0)
2424
}
2525

pkg/cmd/repo/sync/sync.go

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/MakeNowJust/heredoc"
1010
"github.com/cli/cli/api"
1111
"github.com/cli/cli/context"
12+
gitpkg "github.com/cli/cli/git"
1213
"github.com/cli/cli/internal/ghrepo"
1314
"github.com/cli/cli/pkg/cmdutil"
1415
"github.com/cli/cli/pkg/iostreams"
@@ -91,24 +92,20 @@ func syncRun(opts *SyncOptions) error {
9192
}
9293

9394
func syncLocalRepo(opts *SyncOptions) error {
94-
var err error
9595
var srcRepo ghrepo.Interface
9696

97-
dirtyRepo, err := opts.Git.IsDirty()
98-
if err != nil {
99-
return err
100-
}
101-
if dirtyRepo {
102-
return fmt.Errorf("can't sync because there are local changes, please commit or stash them")
103-
}
104-
10597
if opts.SrcArg != "" {
98+
var err error
10699
srcRepo, err = ghrepo.FromFullName(opts.SrcArg)
100+
if err != nil {
101+
return err
102+
}
107103
} else {
104+
var err error
108105
srcRepo, err = opts.BaseRepo()
109-
}
110-
if err != nil {
111-
return err
106+
if err != nil {
107+
return err
108+
}
112109
}
113110

114111
// Find remote that matches the srcRepo
@@ -117,14 +114,9 @@ func syncLocalRepo(opts *SyncOptions) error {
117114
if err != nil {
118115
return err
119116
}
120-
for _, r := range remotes {
121-
if r.RepoName() == srcRepo.RepoName() &&
122-
r.RepoOwner() == srcRepo.RepoOwner() &&
123-
r.RepoHost() == srcRepo.RepoHost() {
124-
remote = r.Name
125-
}
126-
}
127-
if remote == "" {
117+
if r, err := remotes.FindByRepo(srcRepo.RepoOwner(), srcRepo.RepoName()); err == nil {
118+
remote = r.Name
119+
} else {
128120
return fmt.Errorf("can't find corresponding remote for %s", ghrepo.FullName(srcRepo))
129121
}
130122

@@ -238,6 +230,7 @@ var mismatchRemotesError = errors.New("branch remote does not match specified so
238230
func executeLocalRepoSync(srcRepo ghrepo.Interface, remote string, opts *SyncOptions) error {
239231
git := opts.Git
240232
branch := opts.Branch
233+
useForce := opts.Force
241234

242235
if err := git.Fetch(remote, fmt.Sprintf("refs/heads/%s", branch)); err != nil {
243236
return err
@@ -253,47 +246,49 @@ func executeLocalRepoSync(srcRepo ghrepo.Interface, remote string, opts *SyncOpt
253246
return mismatchRemotesError
254247
}
255248

256-
fastForward, err := git.IsAncestor(branch, fmt.Sprintf("%s/%s", remote, branch))
249+
fastForward, err := git.IsAncestor(branch, "FETCH_HEAD")
257250
if err != nil {
258251
return err
259252
}
260253

261-
if !fastForward && !opts.Force {
254+
if !fastForward && !useForce {
262255
return divergingError
263256
}
257+
if fastForward && useForce {
258+
useForce = false
259+
}
264260
}
265261

266-
startBranch, err := git.CurrentBranch()
267-
if err != nil {
262+
currentBranch, err := git.CurrentBranch()
263+
if err != nil && !errors.Is(err, gitpkg.ErrNotOnAnyBranch) {
268264
return err
269265
}
270-
if startBranch != branch {
271-
if hasLocalBranch {
272-
if err := git.CheckoutLocal(branch); err != nil {
266+
if currentBranch == branch {
267+
if isDirty, err := git.IsDirty(); err == nil && isDirty {
268+
return fmt.Errorf("can't sync because there are local changes; please stash them before trying again")
269+
} else if err != nil {
270+
return err
271+
}
272+
if useForce {
273+
if err := git.ResetHard("FETCH_HEAD"); err != nil {
273274
return err
274275
}
275276
} else {
276-
if err := git.CheckoutRemote(remote, branch); err != nil {
277+
if err := git.MergeFastForward("FETCH_HEAD"); err != nil {
277278
return err
278279
}
279280
}
280-
}
281-
if hasLocalBranch {
282-
if opts.Force {
283-
if err := git.ResetHard(fmt.Sprintf("refs/remotes/%s/%s", remote, branch)); err != nil {
281+
} else {
282+
if hasLocalBranch {
283+
if err := git.UpdateBranch(branch, "FETCH_HEAD"); err != nil {
284284
return err
285285
}
286286
} else {
287-
if err := git.MergeFastForward(fmt.Sprintf("refs/remotes/%s/%s", remote, branch)); err != nil {
287+
if err := git.CreateBranch(branch, "FETCH_HEAD", fmt.Sprintf("%s/%s", remote, branch)); err != nil {
288288
return err
289289
}
290290
}
291291
}
292-
if startBranch != branch {
293-
if err := git.CheckoutLocal(startBranch); err != nil {
294-
return err
295-
}
296-
}
297292

298293
return nil
299294
}

0 commit comments

Comments
 (0)