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