code.gitea.io/gitea@v1.22.3/services/mirror/mirror_pull.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package mirror 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 "time" 11 12 repo_model "code.gitea.io/gitea/models/repo" 13 system_model "code.gitea.io/gitea/models/system" 14 "code.gitea.io/gitea/modules/cache" 15 "code.gitea.io/gitea/modules/git" 16 giturl "code.gitea.io/gitea/modules/git/url" 17 "code.gitea.io/gitea/modules/gitrepo" 18 "code.gitea.io/gitea/modules/lfs" 19 "code.gitea.io/gitea/modules/log" 20 "code.gitea.io/gitea/modules/process" 21 "code.gitea.io/gitea/modules/proxy" 22 repo_module "code.gitea.io/gitea/modules/repository" 23 "code.gitea.io/gitea/modules/setting" 24 "code.gitea.io/gitea/modules/timeutil" 25 "code.gitea.io/gitea/modules/util" 26 notify_service "code.gitea.io/gitea/services/notify" 27 ) 28 29 // gitShortEmptySha Git short empty SHA 30 const gitShortEmptySha = "0000000" 31 32 // UpdateAddress writes new address to Git repository and database 33 func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { 34 u, err := giturl.Parse(addr) 35 if err != nil { 36 return fmt.Errorf("invalid addr: %v", err) 37 } 38 39 remoteName := m.GetRemoteName() 40 repoPath := m.GetRepository(ctx).RepoPath() 41 // Remove old remote 42 _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) 43 if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { 44 return err 45 } 46 47 cmd := git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) 48 if strings.Contains(addr, "://") && strings.Contains(addr, "@") { 49 cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(addr), repoPath)) 50 } else { 51 cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath)) 52 } 53 _, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath}) 54 if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { 55 return err 56 } 57 58 if m.Repo.HasWiki() { 59 wikiPath := m.Repo.WikiPath() 60 wikiRemotePath := repo_module.WikiRemoteURL(ctx, addr) 61 // Remove old remote of wiki 62 _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: wikiPath}) 63 if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { 64 return err 65 } 66 67 cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(wikiRemotePath) 68 if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") { 69 cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.SanitizeCredentialURLs(wikiRemotePath), wikiPath)) 70 } else { 71 cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath)) 72 } 73 _, _, err = cmd.RunStdString(&git.RunOpts{Dir: wikiPath}) 74 if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { 75 return err 76 } 77 } 78 79 // erase authentication before storing in database 80 u.User = nil 81 m.Repo.OriginalURL = u.String() 82 return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url") 83 } 84 85 // mirrorSyncResult contains information of a updated reference. 86 // If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. 87 // If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. 88 type mirrorSyncResult struct { 89 refName git.RefName 90 oldCommitID string 91 newCommitID string 92 } 93 94 // parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. 95 // possible output example: 96 /* 97 // * [new tag] v0.1.8 -> v0.1.8 98 // * [new branch] master -> origin/master 99 // - [deleted] (none) -> origin/test // delete a branch 100 // - [deleted] (none) -> 1 // delete a tag 101 // 957a993..a87ba5f test -> origin/test 102 // + f895a1e...957a993 test -> origin/test (forced update) 103 */ 104 // TODO: return whether it's a force update 105 func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { 106 results := make([]*mirrorSyncResult, 0, 3) 107 lines := strings.Split(output, "\n") 108 for i := range lines { 109 // Make sure reference name is presented before continue 110 idx := strings.Index(lines[i], "-> ") 111 if idx == -1 { 112 continue 113 } 114 115 refName := strings.TrimSpace(lines[i][idx+3:]) 116 117 switch { 118 case strings.HasPrefix(lines[i], " * [new tag]"): // new tag 119 results = append(results, &mirrorSyncResult{ 120 refName: git.RefNameFromTag(refName), 121 oldCommitID: gitShortEmptySha, 122 }) 123 case strings.HasPrefix(lines[i], " * [new branch]"): // new branch 124 refName = strings.TrimPrefix(refName, remoteName+"/") 125 results = append(results, &mirrorSyncResult{ 126 refName: git.RefNameFromBranch(refName), 127 oldCommitID: gitShortEmptySha, 128 }) 129 case strings.HasPrefix(lines[i], " - "): // Delete reference 130 isTag := !strings.HasPrefix(refName, remoteName+"/") 131 var refFullName git.RefName 132 if isTag { 133 refFullName = git.RefNameFromTag(refName) 134 } else { 135 refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) 136 } 137 results = append(results, &mirrorSyncResult{ 138 refName: refFullName, 139 newCommitID: gitShortEmptySha, 140 }) 141 case strings.HasPrefix(lines[i], " + "): // Force update 142 if idx := strings.Index(refName, " "); idx > -1 { 143 refName = refName[:idx] 144 } 145 delimIdx := strings.Index(lines[i][3:], " ") 146 if delimIdx == -1 { 147 log.Error("SHA delimiter not found: %q", lines[i]) 148 continue 149 } 150 shas := strings.Split(lines[i][3:delimIdx+3], "...") 151 if len(shas) != 2 { 152 log.Error("Expect two SHAs but not what found: %q", lines[i]) 153 continue 154 } 155 results = append(results, &mirrorSyncResult{ 156 refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")), 157 oldCommitID: shas[0], 158 newCommitID: shas[1], 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("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("Expect two SHAs but not what found: %q", lines[i]) 169 continue 170 } 171 results = append(results, &mirrorSyncResult{ 172 refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")), 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 184 func pruneBrokenReferences(ctx context.Context, 185 m *repo_model.Mirror, 186 repoPath string, 187 timeout time.Duration, 188 stdoutBuilder, stderrBuilder *strings.Builder, 189 isWiki bool, 190 ) error { 191 wiki := "" 192 if isWiki { 193 wiki = "Wiki " 194 } 195 196 stderrBuilder.Reset() 197 stdoutBuilder.Reset() 198 pruneErr := git.NewCommand(ctx, "remote", "prune").AddDynamicArguments(m.GetRemoteName()). 199 SetDescription(fmt.Sprintf("Mirror.runSync %ssPrune references: %s ", wiki, m.Repo.FullName())). 200 Run(&git.RunOpts{ 201 Timeout: timeout, 202 Dir: repoPath, 203 Stdout: stdoutBuilder, 204 Stderr: stderrBuilder, 205 }) 206 if pruneErr != nil { 207 stdout := stdoutBuilder.String() 208 stderr := stderrBuilder.String() 209 210 // sanitize the output, since it may contain the remote address, which may 211 // contain a password 212 stderrMessage := util.SanitizeCredentialURLs(stderr) 213 stdoutMessage := util.SanitizeCredentialURLs(stdout) 214 215 log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr) 216 desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage) 217 if err := system_model.CreateRepositoryNotice(desc); err != nil { 218 log.Error("CreateRepositoryNotice: %v", err) 219 } 220 // this if will only be reached on a successful prune so try to get the mirror again 221 } 222 return pruneErr 223 } 224 225 // runSync returns true if sync finished without error. 226 func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) { 227 repoPath := m.Repo.RepoPath() 228 wikiPath := m.Repo.WikiPath() 229 timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second 230 231 log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) 232 233 // use fetch but not remote update because git fetch support --tags but remote update doesn't 234 cmd := git.NewCommand(ctx, "fetch") 235 if m.EnablePrune { 236 cmd.AddArguments("--prune") 237 } 238 cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName()) 239 240 remoteURL, remoteErr := git.GetRemoteURL(ctx, repoPath, m.GetRemoteName()) 241 if remoteErr != nil { 242 log.Error("SyncMirrors [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr) 243 return nil, false 244 } 245 246 envs := proxy.EnvWithProxy(remoteURL.URL) 247 248 stdoutBuilder := strings.Builder{} 249 stderrBuilder := strings.Builder{} 250 if err := cmd. 251 SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). 252 Run(&git.RunOpts{ 253 Timeout: timeout, 254 Dir: repoPath, 255 Env: envs, 256 Stdout: &stdoutBuilder, 257 Stderr: &stderrBuilder, 258 }); err != nil { 259 stdout := stdoutBuilder.String() 260 stderr := stderrBuilder.String() 261 262 // sanitize the output, since it may contain the remote address, which may contain a password 263 stderrMessage := util.SanitizeCredentialURLs(stderr) 264 stdoutMessage := util.SanitizeCredentialURLs(stdout) 265 266 // Now check if the error is a resolve reference due to broken reference 267 if strings.Contains(stderr, "unable to resolve reference") && strings.Contains(stderr, "reference broken") { 268 log.Warn("SyncMirrors [repo: %-v]: failed to update mirror repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err) 269 err = nil 270 271 // Attempt prune 272 pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, false) 273 if pruneErr == nil { 274 // Successful prune - reattempt mirror 275 stderrBuilder.Reset() 276 stdoutBuilder.Reset() 277 if err = cmd. 278 SetDescription(fmt.Sprintf("Mirror.runSync: %s", m.Repo.FullName())). 279 Run(&git.RunOpts{ 280 Timeout: timeout, 281 Dir: repoPath, 282 Stdout: &stdoutBuilder, 283 Stderr: &stderrBuilder, 284 }); err != nil { 285 stdout := stdoutBuilder.String() 286 stderr := stderrBuilder.String() 287 288 // sanitize the output, since it may contain the remote address, which may 289 // contain a password 290 stderrMessage = util.SanitizeCredentialURLs(stderr) 291 stdoutMessage = util.SanitizeCredentialURLs(stdout) 292 } 293 } 294 } 295 296 // If there is still an error (or there always was an error) 297 if err != nil { 298 log.Error("SyncMirrors [repo: %-v]: failed to update mirror repository:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) 299 desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage) 300 if err = system_model.CreateRepositoryNotice(desc); err != nil { 301 log.Error("CreateRepositoryNotice: %v", err) 302 } 303 return nil, false 304 } 305 } 306 output := stderrBuilder.String() 307 308 if err := git.WriteCommitGraph(ctx, repoPath); err != nil { 309 log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err) 310 } 311 312 gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo) 313 if err != nil { 314 log.Error("SyncMirrors [repo: %-v]: failed to OpenRepository: %v", m.Repo, err) 315 return nil, false 316 } 317 318 log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo) 319 if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil { 320 log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err) 321 } 322 323 log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) 324 if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil { 325 log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err) 326 } 327 328 if m.LFS && setting.LFS.StartServer { 329 log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo) 330 endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint) 331 lfsClient := lfs.NewClient(endpoint, nil) 332 if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil { 333 log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo, err) 334 } 335 } 336 gitRepo.Close() 337 338 log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) 339 if err := repo_module.UpdateRepoSize(ctx, m.Repo); err != nil { 340 log.Error("SyncMirrors [repo: %-v]: failed to update size for mirror repository: %v", m.Repo, err) 341 } 342 343 if m.Repo.HasWiki() { 344 log.Trace("SyncMirrors [repo: %-v Wiki]: running git remote update...", m.Repo) 345 stderrBuilder.Reset() 346 stdoutBuilder.Reset() 347 if err := git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). 348 SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). 349 Run(&git.RunOpts{ 350 Timeout: timeout, 351 Dir: wikiPath, 352 Stdout: &stdoutBuilder, 353 Stderr: &stderrBuilder, 354 }); err != nil { 355 stdout := stdoutBuilder.String() 356 stderr := stderrBuilder.String() 357 358 // sanitize the output, since it may contain the remote address, which may contain a password 359 stderrMessage := util.SanitizeCredentialURLs(stderr) 360 stdoutMessage := util.SanitizeCredentialURLs(stdout) 361 362 // Now check if the error is a resolve reference due to broken reference 363 if strings.Contains(stderrMessage, "unable to resolve reference") && strings.Contains(stderrMessage, "reference broken") { 364 log.Warn("SyncMirrors [repo: %-v Wiki]: failed to update mirror wiki repository due to broken references:\nStdout: %s\nStderr: %s\nErr: %v\nAttempting Prune", m.Repo, stdoutMessage, stderrMessage, err) 365 err = nil 366 367 // Attempt prune 368 pruneErr := pruneBrokenReferences(ctx, m, repoPath, timeout, &stdoutBuilder, &stderrBuilder, true) 369 if pruneErr == nil { 370 // Successful prune - reattempt mirror 371 stderrBuilder.Reset() 372 stdoutBuilder.Reset() 373 374 if err = git.NewCommand(ctx, "remote", "update", "--prune").AddDynamicArguments(m.GetRemoteName()). 375 SetDescription(fmt.Sprintf("Mirror.runSync Wiki: %s ", m.Repo.FullName())). 376 Run(&git.RunOpts{ 377 Timeout: timeout, 378 Dir: wikiPath, 379 Stdout: &stdoutBuilder, 380 Stderr: &stderrBuilder, 381 }); err != nil { 382 stdout := stdoutBuilder.String() 383 stderr := stderrBuilder.String() 384 stderrMessage = util.SanitizeCredentialURLs(stderr) 385 stdoutMessage = util.SanitizeCredentialURLs(stdout) 386 } 387 } 388 } 389 390 // If there is still an error (or there always was an error) 391 if err != nil { 392 log.Error("SyncMirrors [repo: %-v Wiki]: failed to update mirror repository wiki:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err) 393 desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage) 394 if err = system_model.CreateRepositoryNotice(desc); err != nil { 395 log.Error("CreateRepositoryNotice: %v", err) 396 } 397 return nil, false 398 } 399 400 if err := git.WriteCommitGraph(ctx, wikiPath); err != nil { 401 log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err) 402 } 403 } 404 log.Trace("SyncMirrors [repo: %-v Wiki]: git remote update complete", m.Repo) 405 } 406 407 log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo) 408 branches, _, err := gitrepo.GetBranchesByPath(ctx, m.Repo, 0, 0) 409 if err != nil { 410 log.Error("SyncMirrors [repo: %-v]: failed to GetBranches: %v", m.Repo, err) 411 return nil, false 412 } 413 414 for _, branch := range branches { 415 cache.Remove(m.Repo.GetCommitsCountCacheKey(branch.Name, true)) 416 } 417 418 m.UpdatedUnix = timeutil.TimeStampNow() 419 return parseRemoteUpdateOutput(output, m.GetRemoteName()), true 420 } 421 422 // SyncPullMirror starts the sync of the pull mirror and schedules the next run. 423 func SyncPullMirror(ctx context.Context, repoID int64) bool { 424 log.Trace("SyncMirrors [repo_id: %v]", repoID) 425 defer func() { 426 err := recover() 427 if err == nil { 428 return 429 } 430 // There was a panic whilst syncMirrors... 431 log.Error("PANIC whilst SyncMirrors[repo_id: %d] Panic: %v\nStacktrace: %s", repoID, err, log.Stack(2)) 432 }() 433 434 m, err := repo_model.GetMirrorByRepoID(ctx, repoID) 435 if err != nil { 436 log.Error("SyncMirrors [repo_id: %v]: unable to GetMirrorByRepoID: %v", repoID, err) 437 return false 438 } 439 _ = m.GetRepository(ctx) // force load repository of mirror 440 441 ctx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Syncing Mirror %s/%s", m.Repo.OwnerName, m.Repo.Name)) 442 defer finished() 443 444 log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo) 445 results, ok := runSync(ctx, m) 446 if !ok { 447 if err = repo_model.TouchMirror(ctx, m); err != nil { 448 log.Error("SyncMirrors [repo: %-v]: failed to TouchMirror: %v", m.Repo, err) 449 } 450 return false 451 } 452 453 log.Trace("SyncMirrors [repo: %-v]: Scheduling next update", m.Repo) 454 m.ScheduleNextUpdate() 455 if err = repo_model.UpdateMirror(ctx, m); err != nil { 456 log.Error("SyncMirrors [repo: %-v]: failed to UpdateMirror with next update date: %v", m.Repo, err) 457 return false 458 } 459 460 gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo) 461 if err != nil { 462 log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) 463 return false 464 } 465 defer gitRepo.Close() 466 467 log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) 468 if len(results) > 0 { 469 if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok { 470 log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) 471 return false 472 } 473 } 474 475 for _, result := range results { 476 // Discard GitHub pull requests, i.e. refs/pull/* 477 if result.refName.IsPull() { 478 continue 479 } 480 481 // Create reference 482 if result.oldCommitID == gitShortEmptySha { 483 commitID, err := gitRepo.GetRefCommitID(result.refName.String()) 484 if err != nil { 485 log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) 486 continue 487 } 488 objectFormat := git.ObjectFormatFromName(m.Repo.ObjectFormatName) 489 notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ 490 RefFullName: result.refName, 491 OldCommitID: objectFormat.EmptyObjectID().String(), 492 NewCommitID: commitID, 493 }, repo_module.NewPushCommits()) 494 notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID) 495 continue 496 } 497 498 // Delete reference 499 if result.newCommitID == gitShortEmptySha { 500 notify_service.SyncDeleteRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName) 501 continue 502 } 503 504 // Push commits 505 oldCommitID, err := git.GetFullCommitID(gitRepo.Ctx, gitRepo.Path, result.oldCommitID) 506 if err != nil { 507 log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID[%s]: %v", m.Repo, result.oldCommitID, err) 508 continue 509 } 510 newCommitID, err := git.GetFullCommitID(gitRepo.Ctx, gitRepo.Path, result.newCommitID) 511 if err != nil { 512 log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID [%s]: %v", m.Repo, result.newCommitID, err) 513 continue 514 } 515 commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) 516 if err != nil { 517 log.Error("SyncMirrors [repo: %-v]: unable to get CommitsBetweenIDs [new_commit_id: %s, old_commit_id: %s]: %v", m.Repo, newCommitID, oldCommitID, err) 518 continue 519 } 520 521 theCommits := repo_module.GitToPushCommits(commits) 522 if len(theCommits.Commits) > setting.UI.FeedMaxCommitNum { 523 theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] 524 } 525 526 newCommit, err := gitRepo.GetCommit(newCommitID) 527 if err != nil { 528 log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err) 529 continue 530 } 531 532 theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit) 533 theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) 534 535 notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ 536 RefFullName: result.refName, 537 OldCommitID: oldCommitID, 538 NewCommitID: newCommitID, 539 }, theCommits) 540 } 541 log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) 542 543 isEmpty, err := gitRepo.IsEmpty() 544 if err != nil { 545 log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err) 546 return false 547 } 548 if !isEmpty { 549 // Get latest commit date and update to current repository updated time 550 commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) 551 if err != nil { 552 log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) 553 return false 554 } 555 556 if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { 557 log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) 558 return false 559 } 560 } 561 562 log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) 563 564 return true 565 } 566 567 func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool { 568 if !m.Repo.IsEmpty { 569 return true 570 } 571 572 hasDefault := false 573 hasMaster := false 574 hasMain := false 575 defaultBranchName := m.Repo.DefaultBranch 576 if len(defaultBranchName) == 0 { 577 defaultBranchName = setting.Repository.DefaultBranch 578 } 579 firstName := "" 580 for _, result := range results { 581 if !result.refName.IsBranch() { 582 continue 583 } 584 585 name := result.refName.BranchName() 586 if len(firstName) == 0 { 587 firstName = name 588 } 589 590 hasDefault = hasDefault || name == defaultBranchName 591 hasMaster = hasMaster || name == "master" 592 hasMain = hasMain || name == "main" 593 } 594 595 if len(firstName) > 0 { 596 if hasDefault { 597 m.Repo.DefaultBranch = defaultBranchName 598 } else if hasMaster { 599 m.Repo.DefaultBranch = "master" 600 } else if hasMain { 601 m.Repo.DefaultBranch = "main" 602 } else { 603 m.Repo.DefaultBranch = firstName 604 } 605 // Update the git repository default branch 606 if err := gitrepo.SetDefaultBranch(ctx, m.Repo, m.Repo.DefaultBranch); err != nil { 607 if !git.IsErrUnsupportedVersion(err) { 608 log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err) 609 desc := fmt.Sprintf("Failed to update default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err) 610 if err = system_model.CreateRepositoryNotice(desc); err != nil { 611 log.Error("CreateRepositoryNotice: %v", err) 612 } 613 return false 614 } 615 } 616 m.Repo.IsEmpty = false 617 // Update the is empty and default_branch columns 618 if err := repo_model.UpdateRepositoryCols(ctx, m.Repo, "default_branch", "is_empty"); err != nil { 619 log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err) 620 desc := fmt.Sprintf("Failed to update default branch of repository '%s': %v", m.Repo.RepoPath(), err) 621 if err = system_model.CreateRepositoryNotice(desc); err != nil { 622 log.Error("CreateRepositoryNotice: %v", err) 623 } 624 return false 625 } 626 } 627 return true 628 }