From d3c75ea4d916e0c81de2e8f7c5b1d748af9fa45e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 3 Jul 2018 16:37:17 -0700 Subject: [PATCH] Support creating a remote branch to import closed pull requests --- lib/bitbucket_server/client.rb | 10 ++++ lib/bitbucket_server/connection.rb | 16 ++++- .../bitbucket_server_import/importer.rb | 58 ++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index e632adea68c..49654e45226 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -27,6 +27,16 @@ module BitbucketServer get_collection(path, :repo) end + def create_branch(project_key, repo, branch_name, sha) + payload = { + name: branch_name, + startPoint: sha, + message: 'GitLab temporary branch for import' + } + + connection.post("/projects/#{project_key}/repos/#{repo}/branches", payload.to_json) + end + private def get_collection(path, type) diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 5c3bea3b743..21807c8a229 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -12,7 +12,6 @@ module BitbucketServer end def get(path, extra_query = {}) - auth = { username: username, password: token } response = Gitlab::HTTP.get(build_url(path), basic_auth: auth, params: extra_query) @@ -20,8 +19,23 @@ module BitbucketServer response.parsed_response end + def post(path, body) + Gitlab::HTTP.post(build_url(path), + basic_auth: auth, + headers: post_headers, + body: body) + end + private + def auth + @auth ||= { username: username, password: token } + end + + def post_headers + @post_headers ||= { 'Content-Type' => 'application/json' } + end + def build_url(path) return path if path.starts_with?(root_url) diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 84d16074891..2a33feb68a9 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -4,6 +4,16 @@ module Gitlab include Gitlab::ShellAdapter attr_reader :project, :project_key, :repository_slug, :client, :errors, :users + REMOTE_NAME = 'bitbucket_server'.freeze + + def self.imports_repository? + true + end + + def self.refmap + [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'] + end + def initialize(project) @project = project @project_key = project.import_data.data['project_key'] @@ -12,9 +22,11 @@ module Gitlab @formatter = Gitlab::ImportFormatter.new @errors = [] @users = {} + @temp_branches = [] end def execute + import_repository import_pull_requests handle_errors @@ -48,11 +60,55 @@ module Gitlab @repo ||= client.repo(project_key, repository_slug) end + def sha_exists?(sha) + project.repository.commit(sha) + end + + def track_temp_branch(pull_request, index) + temp_branch_name = "gitlab/import/pull-request/#{pull_request.iid}-#{index}" + + @temp_branches << temp_branch_name + temp_branch_name + end + + def restore_branches(pull_request) + shas_to_restore = [pull_request.source_branch_sha, pull_request.target_branch_sha] + resync = false + + shas_to_restore.each_with_index do |sha, index| + next if sha_exists?(sha) + + branch_name = track_temp_branch(pull_request, index) + response = client.create_branch(project_key, repository_slug, branch_name, sha) + + if response.success? + resync = true + else + Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{response.code}") + end + end + + import_repository if resync + end + + def import_repository + project.ensure_repository + project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap, remote_name: REMOTE_NAME) + rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e + # Expire cache to prevent scenarios such as: + # 1. First import failed, but the repo was imported successfully, so +exists?+ returns true + # 2. Retried import, repo is broken or not imported but +exists?+ still returns true + project.repository.expire_content_cache if project.repository_exists? + + raise RuntimeError, e.message + end + def import_pull_requests pull_requests = client.pull_requests(project_key, repository_slug) - pull_requests.each do |pull_request| begin + restore_branches(pull_request) + description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) description += pull_request.description