123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // Copyright 2019 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package repository
-
- import (
- "fmt"
- "os"
- "path"
- "strings"
- "time"
-
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/timeutil"
- "gopkg.in/ini.v1"
- )
-
- /*
- GitHub, GitLab, Gogs: *.wiki.git
- BitBucket: *.git/wiki
- */
- var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"}
-
- // wikiRemoteURL returns accessible repository URL for wiki if exists.
- // Otherwise, it returns an empty string.
- func wikiRemoteURL(remote string) string {
- remote = strings.TrimSuffix(remote, ".git")
- for _, suffix := range commonWikiURLSuffixes {
- wikiURL := remote + suffix
- if git.IsRepoURLAccessible(wikiURL) {
- return wikiURL
- }
- }
- return ""
- }
-
- // MigrateRepositoryGitData starts migrating git related data after created migrating repository
- func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opts api.MigrateRepoOption) (*models.Repository, error) {
- repoPath := models.RepoPath(u.Name, opts.RepoName)
-
- if u.IsOrganization() {
- t, err := u.GetOwnerTeam()
- if err != nil {
- return nil, err
- }
- repo.NumWatches = t.NumMembers
- } else {
- repo.NumWatches = 1
- }
-
- migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
-
- var err error
- if err = os.RemoveAll(repoPath); err != nil {
- return repo, fmt.Errorf("Failed to remove %s: %v", repoPath, err)
- }
-
- if err = git.Clone(opts.CloneAddr, repoPath, git.CloneRepoOptions{
- Mirror: true,
- Quiet: true,
- Timeout: migrateTimeout,
- }); err != nil {
- return repo, fmt.Errorf("Clone: %v", err)
- }
-
- if opts.Wiki {
- wikiPath := models.WikiPath(u.Name, opts.RepoName)
- wikiRemotePath := wikiRemoteURL(opts.CloneAddr)
- if len(wikiRemotePath) > 0 {
- if err := os.RemoveAll(wikiPath); err != nil {
- return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
- }
-
- if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
- Mirror: true,
- Quiet: true,
- Timeout: migrateTimeout,
- Branch: "master",
- }); err != nil {
- log.Warn("Clone wiki: %v", err)
- if err := os.RemoveAll(wikiPath); err != nil {
- return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
- }
- }
- }
- }
-
- gitRepo, err := git.OpenRepository(repoPath)
- if err != nil {
- return repo, fmt.Errorf("OpenRepository: %v", err)
- }
- defer gitRepo.Close()
-
- repo.IsEmpty, err = gitRepo.IsEmpty()
- if err != nil {
- return repo, fmt.Errorf("git.IsEmpty: %v", err)
- }
-
- if !opts.Releases && !repo.IsEmpty {
- // Try to get HEAD branch and set it as default branch.
- headBranch, err := gitRepo.GetHEADBranch()
- if err != nil {
- return repo, fmt.Errorf("GetHEADBranch: %v", err)
- }
- if headBranch != nil {
- repo.DefaultBranch = headBranch.Name
- }
-
- if err = SyncReleasesWithTags(repo, gitRepo); err != nil {
- log.Error("Failed to synchronize tags to releases for repository: %v", err)
- }
- }
-
- if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
- log.Error("Failed to update size for repository: %v", err)
- }
-
- if opts.Mirror {
- if err = models.InsertMirror(&models.Mirror{
- RepoID: repo.ID,
- Interval: setting.Mirror.DefaultInterval,
- EnablePrune: true,
- NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval),
- }); err != nil {
- return repo, fmt.Errorf("InsertOne: %v", err)
- }
-
- repo.IsMirror = true
- err = models.UpdateRepository(repo, false)
- } else {
- repo, err = CleanUpMigrateInfo(repo)
- }
-
- return repo, err
- }
-
- // cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
- // This also removes possible user credentials.
- func cleanUpMigrateGitConfig(configPath string) error {
- cfg, err := ini.Load(configPath)
- if err != nil {
- return fmt.Errorf("open config file: %v", err)
- }
- cfg.DeleteSection("remote \"origin\"")
- if err = cfg.SaveToIndent(configPath, "\t"); err != nil {
- return fmt.Errorf("save config file: %v", err)
- }
- return nil
- }
-
- // CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
- func CleanUpMigrateInfo(repo *models.Repository) (*models.Repository, error) {
- repoPath := repo.RepoPath()
- if err := models.CreateDelegateHooks(repoPath); err != nil {
- return repo, fmt.Errorf("createDelegateHooks: %v", err)
- }
- if repo.HasWiki() {
- if err := models.CreateDelegateHooks(repo.WikiPath()); err != nil {
- return repo, fmt.Errorf("createDelegateHooks.(wiki): %v", err)
- }
- }
-
- _, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
- if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
- return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
- }
-
- if repo.HasWiki() {
- if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil {
- return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err)
- }
- }
-
- return repo, models.UpdateRepository(repo, false)
- }
-
- // SyncReleasesWithTags synchronizes release table with repository tags
- func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) error {
- existingRelTags := make(map[string]struct{})
- opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true}
- for page := 1; ; page++ {
- rels, err := models.GetReleasesByRepoID(repo.ID, opts, page, 100)
- if err != nil {
- return fmt.Errorf("GetReleasesByRepoID: %v", err)
- }
- if len(rels) == 0 {
- break
- }
- for _, rel := range rels {
- if rel.IsDraft {
- continue
- }
- commitID, err := gitRepo.GetTagCommitID(rel.TagName)
- if err != nil && !git.IsErrNotExist(err) {
- return fmt.Errorf("GetTagCommitID: %s: %v", rel.TagName, err)
- }
- if git.IsErrNotExist(err) || commitID != rel.Sha1 {
- if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
- return fmt.Errorf("PushUpdateDeleteTag: %s: %v", rel.TagName, err)
- }
- } else {
- existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
- }
- }
- }
- tags, err := gitRepo.GetTags()
- if err != nil {
- return fmt.Errorf("GetTags: %v", err)
- }
- for _, tagName := range tags {
- if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
- if err := PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
- return fmt.Errorf("pushUpdateAddTag: %v", err)
- }
- }
- }
- return nil
- }
-
- // PushUpdateAddTag must be called for any push actions to add tag
- func PushUpdateAddTag(repo *models.Repository, gitRepo *git.Repository, tagName string) error {
- tag, err := gitRepo.GetTag(tagName)
- if err != nil {
- return fmt.Errorf("GetTag: %v", err)
- }
- commit, err := tag.Commit()
- if err != nil {
- return fmt.Errorf("Commit: %v", err)
- }
-
- sig := tag.Tagger
- if sig == nil {
- sig = commit.Author
- }
- if sig == nil {
- sig = commit.Committer
- }
-
- var author *models.User
- var createdAt = time.Unix(1, 0)
-
- if sig != nil {
- author, err = models.GetUserByEmail(sig.Email)
- if err != nil && !models.IsErrUserNotExist(err) {
- return fmt.Errorf("GetUserByEmail: %v", err)
- }
- createdAt = sig.When
- }
-
- commitsCount, err := commit.CommitsCount()
- if err != nil {
- return fmt.Errorf("CommitsCount: %v", err)
- }
-
- var rel = models.Release{
- RepoID: repo.ID,
- TagName: tagName,
- LowerTagName: strings.ToLower(tagName),
- Sha1: commit.ID.String(),
- NumCommits: commitsCount,
- CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
- IsTag: true,
- }
- if author != nil {
- rel.PublisherID = author.ID
- }
-
- return models.SaveOrUpdateTag(repo, &rel)
- }
|