1
1
// Copyright 2016 The Gogs Authors. All rights reserved.
2
+ // Copyright 2018 The Gitea Authors. All rights reserved.
2
3
// Use of this source code is governed by a MIT-style
3
4
// license that can be found in the LICENSE file.
4
5
5
6
package models
6
7
7
8
import (
8
9
"fmt"
10
+ "strings"
9
11
"time"
10
12
11
13
"code.gitea.io/git"
@@ -119,8 +121,68 @@ func (m *Mirror) SaveAddress(addr string) error {
119
121
return cfg .SaveToIndent (configPath , "\t " )
120
122
}
121
123
124
+ // gitShortEmptySha Git short empty SHA
125
+ const gitShortEmptySha = "0000000"
126
+
127
+ // mirrorSyncResult contains information of a updated reference.
128
+ // If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty.
129
+ // If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty.
130
+ type mirrorSyncResult struct {
131
+ refName string
132
+ oldCommitID string
133
+ newCommitID string
134
+ }
135
+
136
+ // parseRemoteUpdateOutput detects create, update and delete operations of references from upstream.
137
+ func parseRemoteUpdateOutput (output string ) []* mirrorSyncResult {
138
+ results := make ([]* mirrorSyncResult , 0 , 3 )
139
+ lines := strings .Split (output , "\n " )
140
+ for i := range lines {
141
+ // Make sure reference name is presented before continue
142
+ idx := strings .Index (lines [i ], "-> " )
143
+ if idx == - 1 {
144
+ continue
145
+ }
146
+
147
+ refName := lines [i ][idx + 3 :]
148
+
149
+ switch {
150
+ case strings .HasPrefix (lines [i ], " * " ): // New reference
151
+ results = append (results , & mirrorSyncResult {
152
+ refName : refName ,
153
+ oldCommitID : gitShortEmptySha ,
154
+ })
155
+ case strings .HasPrefix (lines [i ], " - " ): // Delete reference
156
+ results = append (results , & mirrorSyncResult {
157
+ refName : refName ,
158
+ newCommitID : gitShortEmptySha ,
159
+ })
160
+ case strings .HasPrefix (lines [i ], " " ): // New commits of a reference
161
+ delimIdx := strings .Index (lines [i ][3 :], " " )
162
+ if delimIdx == - 1 {
163
+ log .Error (2 , "SHA delimiter not found: %q" , lines [i ])
164
+ continue
165
+ }
166
+ shas := strings .Split (lines [i ][3 :delimIdx + 3 ], ".." )
167
+ if len (shas ) != 2 {
168
+ log .Error (2 , "Expect two SHAs but not what found: %q" , lines [i ])
169
+ continue
170
+ }
171
+ results = append (results , & mirrorSyncResult {
172
+ refName : refName ,
173
+ oldCommitID : shas [0 ],
174
+ newCommitID : shas [1 ],
175
+ })
176
+
177
+ default :
178
+ log .Warn ("parseRemoteUpdateOutput: unexpected update line %q" , lines [i ])
179
+ }
180
+ }
181
+ return results
182
+ }
183
+
122
184
// runSync returns true if sync finished without error.
123
- func (m * Mirror ) runSync () bool {
185
+ func (m * Mirror ) runSync () ([] * mirrorSyncResult , bool ) {
124
186
repoPath := m .Repo .RepoPath ()
125
187
wikiPath := m .Repo .WikiPath ()
126
188
timeout := time .Duration (setting .Git .Timeout .Mirror ) * time .Second
@@ -130,28 +192,30 @@ func (m *Mirror) runSync() bool {
130
192
gitArgs = append (gitArgs , "--prune" )
131
193
}
132
194
133
- if _ , stderr , err := process .GetManager ().ExecDir (
195
+ _ , stderr , err := process .GetManager ().ExecDir (
134
196
timeout , repoPath , fmt .Sprintf ("Mirror.runSync: %s" , repoPath ),
135
- "git" , gitArgs ... ); err != nil {
197
+ "git" , gitArgs ... )
198
+ if err != nil {
136
199
// sanitize the output, since it may contain the remote address, which may
137
200
// contain a password
138
201
message , err := sanitizeOutput (stderr , repoPath )
139
202
if err != nil {
140
203
log .Error (4 , "sanitizeOutput: %v" , err )
141
- return false
204
+ return nil , false
142
205
}
143
206
desc := fmt .Sprintf ("Failed to update mirror repository '%s': %s" , repoPath , message )
144
207
log .Error (4 , desc )
145
208
if err = CreateRepositoryNotice (desc ); err != nil {
146
209
log .Error (4 , "CreateRepositoryNotice: %v" , err )
147
210
}
148
- return false
211
+ return nil , false
149
212
}
213
+ output := stderr
150
214
151
215
gitRepo , err := git .OpenRepository (repoPath )
152
216
if err != nil {
153
217
log .Error (4 , "OpenRepository: %v" , err )
154
- return false
218
+ return nil , false
155
219
}
156
220
if err = SyncReleasesWithTags (m .Repo , gitRepo ); err != nil {
157
221
log .Error (4 , "Failed to synchronize tags to releases for repository: %v" , err )
@@ -170,29 +234,29 @@ func (m *Mirror) runSync() bool {
170
234
message , err := sanitizeOutput (stderr , wikiPath )
171
235
if err != nil {
172
236
log .Error (4 , "sanitizeOutput: %v" , err )
173
- return false
237
+ return nil , false
174
238
}
175
239
desc := fmt .Sprintf ("Failed to update mirror wiki repository '%s': %s" , wikiPath , message )
176
240
log .Error (4 , desc )
177
241
if err = CreateRepositoryNotice (desc ); err != nil {
178
242
log .Error (4 , "CreateRepositoryNotice: %v" , err )
179
243
}
180
- return false
244
+ return nil , false
181
245
}
182
246
}
183
247
184
248
branches , err := m .Repo .GetBranches ()
185
249
if err != nil {
186
250
log .Error (4 , "GetBranches: %v" , err )
187
- return false
251
+ return nil , false
188
252
}
189
253
190
254
for i := range branches {
191
255
cache .Remove (m .Repo .GetCommitsCountCacheKey (branches [i ].Name , true ))
192
256
}
193
257
194
258
m .UpdatedUnix = util .TimeStampNow ()
195
- return true
259
+ return parseRemoteUpdateOutput ( output ), true
196
260
}
197
261
198
262
func getMirrorByRepoID (e Engine , repoID int64 ) (* Mirror , error ) {
@@ -268,7 +332,8 @@ func SyncMirrors() {
268
332
continue
269
333
}
270
334
271
- if ! m .runSync () {
335
+ results , ok := m .runSync ()
336
+ if ! ok {
272
337
continue
273
338
}
274
339
@@ -278,6 +343,66 @@ func SyncMirrors() {
278
343
continue
279
344
}
280
345
346
+ var gitRepo * git.Repository
347
+ if len (results ) == 0 {
348
+ log .Trace ("SyncMirrors [repo_id: %d]: no commits fetched" , m .RepoID )
349
+ } else {
350
+ gitRepo , err = git .OpenRepository (m .Repo .RepoPath ())
351
+ if err != nil {
352
+ log .Error (2 , "OpenRepository [%d]: %v" , m .RepoID , err )
353
+ continue
354
+ }
355
+ }
356
+
357
+ for _ , result := range results {
358
+ // Discard GitHub pull requests, i.e. refs/pull/*
359
+ if strings .HasPrefix (result .refName , "refs/pull/" ) {
360
+ continue
361
+ }
362
+
363
+ // Create reference
364
+ if result .oldCommitID == gitShortEmptySha {
365
+ if err = MirrorSyncCreateAction (m .Repo , result .refName ); err != nil {
366
+ log .Error (2 , "MirrorSyncCreateAction [repo_id: %d]: %v" , m .RepoID , err )
367
+ }
368
+ continue
369
+ }
370
+
371
+ // Delete reference
372
+ if result .newCommitID == gitShortEmptySha {
373
+ if err = MirrorSyncDeleteAction (m .Repo , result .refName ); err != nil {
374
+ log .Error (2 , "MirrorSyncDeleteAction [repo_id: %d]: %v" , m .RepoID , err )
375
+ }
376
+ continue
377
+ }
378
+
379
+ // Push commits
380
+ oldCommitID , err := git .GetFullCommitID (gitRepo .Path , result .oldCommitID )
381
+ if err != nil {
382
+ log .Error (2 , "GetFullCommitID [%d]: %v" , m .RepoID , err )
383
+ continue
384
+ }
385
+ newCommitID , err := git .GetFullCommitID (gitRepo .Path , result .newCommitID )
386
+ if err != nil {
387
+ log .Error (2 , "GetFullCommitID [%d]: %v" , m .RepoID , err )
388
+ continue
389
+ }
390
+ commits , err := gitRepo .CommitsBetweenIDs (newCommitID , oldCommitID )
391
+ if err != nil {
392
+ log .Error (2 , "CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v" , m .RepoID , newCommitID , oldCommitID , err )
393
+ continue
394
+ }
395
+ if err = MirrorSyncPushAction (m .Repo , MirrorSyncPushActionOptions {
396
+ RefName : result .refName ,
397
+ OldCommitID : oldCommitID ,
398
+ NewCommitID : newCommitID ,
399
+ Commits : ListToPushCommits (commits ),
400
+ }); err != nil {
401
+ log .Error (2 , "MirrorSyncPushAction [repo_id: %d]: %v" , m .RepoID , err )
402
+ continue
403
+ }
404
+ }
405
+
281
406
// Get latest commit date and update to current repository updated time
282
407
commitDate , err := git .GetLatestCommitTime (m .Repo .RepoPath ())
283
408
if err != nil {
0 commit comments