@@ -55,6 +55,7 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
55
55
return false , fmt .Errorf ("parse url %q: %w" , opts .RepoURL , err )
56
56
}
57
57
logf ("Parsed Git URL as %q" , parsed .Redacted ())
58
+ opts .RepoURL = parsed .String ()
58
59
if parsed .Hostname () == "dev.azure.com" {
59
60
// Azure DevOps requires capabilities multi_ack / multi_ack_detailed,
60
61
// which are not fully implemented and by default are included in
@@ -100,19 +101,17 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
100
101
gitStorage := filesystem .NewStorage (gitDir , cache .NewObjectLRU (cache .DefaultMaxSize * 10 ))
101
102
fsStorage := filesystem .NewStorage (fs , cache .NewObjectLRU (cache .DefaultMaxSize * 10 ))
102
103
repo , err := git .Open (fsStorage , gitDir )
103
- if errors .Is (err , git .ErrRepositoryNotExists ) {
104
- err = nil
104
+ if err == nil {
105
+ // Repo exists, so fast-forward it.
106
+ return fastForwardRepo (ctx , logf , opts , repo , reference )
105
107
}
106
- if err != nil {
108
+
109
+ // Something went wrong opening the repo.
110
+ if ! errors .Is (err , git .ErrRepositoryNotExists ) {
107
111
return false , fmt .Errorf ("open %q: %w" , opts .RepoURL , err )
108
112
}
109
- if repo != nil {
110
- if head , err := repo .Head (); err == nil && head != nil {
111
- logf ("existing repo HEAD: %s" , head .Hash ().String ())
112
- }
113
- return false , nil
114
- }
115
113
114
+ // Repo does not exist, so clone it.
116
115
repo , err = git .CloneContext (ctx , gitStorage , fs , & git.CloneOptions {
117
116
URL : parsed .String (),
118
117
Auth : opts .RepoAuth ,
@@ -136,6 +135,40 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
136
135
return true , nil
137
136
}
138
137
138
+ func fastForwardRepo (ctx context.Context , logf func (string , ... any ), opts CloneRepoOptions , repo * git.Repository , referenceName string ) (bool , error ) {
139
+ if head , err := repo .Head (); err == nil && head != nil {
140
+ logf ("existing repo HEAD: %s" , head .Hash ().String ())
141
+ }
142
+ wt , err := repo .Worktree ()
143
+ if err != nil {
144
+ return false , fmt .Errorf ("worktree: %w" , err )
145
+ }
146
+ err = wt .PullContext (ctx , & git.PullOptions {
147
+ RemoteName : "" , // use default remote
148
+ ReferenceName : plumbing .ReferenceName (referenceName ),
149
+ SingleBranch : opts .SingleBranch ,
150
+ Depth : opts .Depth ,
151
+ Auth : opts .RepoAuth ,
152
+ Progress : opts .Progress ,
153
+ Force : false ,
154
+ InsecureSkipTLS : opts .Insecure ,
155
+ CABundle : opts .CABundle ,
156
+ ProxyOptions : opts .ProxyOptions ,
157
+ })
158
+ if err == nil {
159
+ if head , err := repo .Head (); err == nil && head != nil {
160
+ logf ("fast-forwarded to %s" , head .Hash ().String ())
161
+ }
162
+ return true , nil
163
+ }
164
+ if errors .Is (err , git .NoErrAlreadyUpToDate ) {
165
+ logf ("existing repo already up-to-date" )
166
+ return false , nil
167
+ }
168
+ logf ("failed to fast-forward: %s" , err .Error ())
169
+ return false , err
170
+ }
171
+
139
172
// ShallowCloneRepo will clone the repository at the given URL into the given path
140
173
// with a depth of 1. If the destination folder exists and is not empty, the
141
174
// clone will not be performed.
0 commit comments