2019-05-06 18:12:51 -07:00
|
|
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
|
|
// Copyright 2018 Jonas Franz. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package migrations
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"code.gitea.io/gitea/models"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
|
|
"code.gitea.io/gitea/modules/migrations/base"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MigrateOptions is equal to base.MigrateOptions
|
|
|
|
type MigrateOptions = base.MigrateOptions
|
|
|
|
|
|
|
|
var (
|
|
|
|
factories []base.DownloaderFactory
|
|
|
|
)
|
|
|
|
|
|
|
|
// RegisterDownloaderFactory registers a downloader factory
|
|
|
|
func RegisterDownloaderFactory(factory base.DownloaderFactory) {
|
|
|
|
factories = append(factories, factory)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MigrateRepository migrate repository according MigrateOptions
|
|
|
|
func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) {
|
|
|
|
var (
|
|
|
|
downloader base.Downloader
|
|
|
|
uploader = NewGiteaLocalUploader(doer, ownerName, opts.Name)
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, factory := range factories {
|
|
|
|
if match, err := factory.Match(opts); err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if match {
|
|
|
|
downloader, err = factory.New(opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if downloader == nil {
|
|
|
|
opts.Wiki = true
|
|
|
|
opts.Milestones = false
|
|
|
|
opts.Labels = false
|
|
|
|
opts.Releases = false
|
|
|
|
opts.Comments = false
|
|
|
|
opts.Issues = false
|
|
|
|
opts.PullRequests = false
|
|
|
|
downloader = NewPlainGitDownloader(ownerName, opts.Name, opts.RemoteURL)
|
|
|
|
log.Trace("Will migrate from git: %s", opts.RemoteURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := migrateRepository(downloader, uploader, opts); err != nil {
|
|
|
|
if err1 := uploader.Rollback(); err1 != nil {
|
|
|
|
log.Error("rollback failed: %v", err1)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return uploader.repo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// migrateRepository will download informations and upload to Uploader, this is a simple
|
|
|
|
// process for small repository. For a big repository, save all the data to disk
|
|
|
|
// before upload is better
|
|
|
|
func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions) error {
|
|
|
|
repo, err := downloader.GetRepoInfo()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
repo.IsPrivate = opts.Private
|
|
|
|
repo.IsMirror = opts.Mirror
|
2019-05-20 05:43:43 -07:00
|
|
|
if opts.Description != "" {
|
|
|
|
repo.Description = opts.Description
|
|
|
|
}
|
2019-05-06 18:12:51 -07:00
|
|
|
log.Trace("migrating git data")
|
2019-07-01 14:17:16 -07:00
|
|
|
if err := uploader.CreateRepo(repo, opts); err != nil {
|
2019-05-06 18:12:51 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Milestones {
|
|
|
|
log.Trace("migrating milestones")
|
|
|
|
milestones, err := downloader.GetMilestones()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
msBatchSize := uploader.MaxBatchInsertSize("milestone")
|
|
|
|
for len(milestones) > 0 {
|
|
|
|
if len(milestones) < msBatchSize {
|
|
|
|
msBatchSize = len(milestones)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := uploader.CreateMilestones(milestones...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
milestones = milestones[msBatchSize:]
|
2019-05-06 18:12:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Labels {
|
|
|
|
log.Trace("migrating labels")
|
|
|
|
labels, err := downloader.GetLabels()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
lbBatchSize := uploader.MaxBatchInsertSize("label")
|
|
|
|
for len(labels) > 0 {
|
|
|
|
if len(labels) < lbBatchSize {
|
|
|
|
lbBatchSize = len(labels)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := uploader.CreateLabels(labels...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
labels = labels[lbBatchSize:]
|
2019-05-06 18:12:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.Releases {
|
|
|
|
log.Trace("migrating releases")
|
|
|
|
releases, err := downloader.GetReleases()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
relBatchSize := uploader.MaxBatchInsertSize("release")
|
|
|
|
for len(releases) > 0 {
|
|
|
|
if len(releases) < relBatchSize {
|
|
|
|
relBatchSize = len(releases)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := uploader.CreateReleases(releases[:relBatchSize]...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
releases = releases[relBatchSize:]
|
2019-05-06 18:12:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
var commentBatchSize = uploader.MaxBatchInsertSize("comment")
|
|
|
|
|
2019-05-06 18:12:51 -07:00
|
|
|
if opts.Issues {
|
|
|
|
log.Trace("migrating issues and comments")
|
2019-07-06 12:24:50 -07:00
|
|
|
var issueBatchSize = uploader.MaxBatchInsertSize("issue")
|
|
|
|
|
2019-05-30 13:26:57 -07:00
|
|
|
for i := 1; ; i++ {
|
2019-07-06 12:24:50 -07:00
|
|
|
issues, isEnd, err := downloader.GetIssues(i, issueBatchSize)
|
2019-05-06 18:12:51 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, issue := range issues {
|
|
|
|
if !opts.IgnoreIssueAuthor {
|
|
|
|
issue.Content = fmt.Sprintf("Author: @%s \n\n%s", issue.PosterName, issue.Content)
|
|
|
|
}
|
2019-06-29 06:38:22 -07:00
|
|
|
}
|
2019-05-06 18:12:51 -07:00
|
|
|
|
2019-06-29 06:38:22 -07:00
|
|
|
if err := uploader.CreateIssues(issues...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-06 18:12:51 -07:00
|
|
|
|
2019-06-29 06:38:22 -07:00
|
|
|
if !opts.Comments {
|
|
|
|
continue
|
|
|
|
}
|
2019-05-06 18:12:51 -07:00
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
var allComments = make([]*base.Comment, 0, commentBatchSize)
|
2019-06-29 06:38:22 -07:00
|
|
|
for _, issue := range issues {
|
2019-05-06 18:12:51 -07:00
|
|
|
comments, err := downloader.GetComments(issue.Number)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, comment := range comments {
|
|
|
|
if !opts.IgnoreIssueAuthor {
|
|
|
|
comment.Content = fmt.Sprintf("Author: @%s \n\n%s", comment.PosterName, comment.Content)
|
|
|
|
}
|
2019-06-29 06:38:22 -07:00
|
|
|
}
|
|
|
|
allComments = append(allComments, comments...)
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
if len(allComments) >= commentBatchSize {
|
|
|
|
if err := uploader.CreateComments(allComments[:commentBatchSize]...); err != nil {
|
2019-05-06 18:12:51 -07:00
|
|
|
return err
|
|
|
|
}
|
2019-07-06 12:24:50 -07:00
|
|
|
|
|
|
|
allComments = allComments[commentBatchSize:]
|
2019-06-29 06:38:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(allComments) > 0 {
|
|
|
|
if err := uploader.CreateComments(allComments...); err != nil {
|
|
|
|
return err
|
2019-05-06 18:12:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 13:26:57 -07:00
|
|
|
if isEnd {
|
2019-05-06 18:12:51 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.PullRequests {
|
|
|
|
log.Trace("migrating pull requests and comments")
|
2019-07-06 13:32:15 -07:00
|
|
|
var prBatchSize = uploader.MaxBatchInsertSize("pullrequest")
|
2019-05-30 13:26:57 -07:00
|
|
|
for i := 1; ; i++ {
|
2019-07-06 12:24:50 -07:00
|
|
|
prs, err := downloader.GetPullRequests(i, prBatchSize)
|
2019-05-06 18:12:51 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, pr := range prs {
|
|
|
|
if !opts.IgnoreIssueAuthor {
|
|
|
|
pr.Content = fmt.Sprintf("Author: @%s \n\n%s", pr.PosterName, pr.Content)
|
|
|
|
}
|
2019-06-29 06:38:22 -07:00
|
|
|
}
|
|
|
|
if err := uploader.CreatePullRequests(prs...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-06 18:12:51 -07:00
|
|
|
|
2019-06-29 06:38:22 -07:00
|
|
|
if !opts.Comments {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
var allComments = make([]*base.Comment, 0, commentBatchSize)
|
2019-06-29 06:38:22 -07:00
|
|
|
for _, pr := range prs {
|
2019-05-06 18:12:51 -07:00
|
|
|
comments, err := downloader.GetComments(pr.Number)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, comment := range comments {
|
|
|
|
if !opts.IgnoreIssueAuthor {
|
|
|
|
comment.Content = fmt.Sprintf("Author: @%s \n\n%s", comment.PosterName, comment.Content)
|
|
|
|
}
|
2019-06-29 06:38:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
allComments = append(allComments, comments...)
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
if len(allComments) >= commentBatchSize {
|
|
|
|
if err := uploader.CreateComments(allComments[:commentBatchSize]...); err != nil {
|
2019-05-06 18:12:51 -07:00
|
|
|
return err
|
|
|
|
}
|
2019-07-06 12:24:50 -07:00
|
|
|
allComments = allComments[commentBatchSize:]
|
2019-05-06 18:12:51 -07:00
|
|
|
}
|
|
|
|
}
|
2019-06-29 06:38:22 -07:00
|
|
|
if len(allComments) > 0 {
|
|
|
|
if err := uploader.CreateComments(allComments...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-06 12:24:50 -07:00
|
|
|
if len(prs) < prBatchSize {
|
2019-05-06 18:12:51 -07:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|