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  }