Commit d9ba9edd authored by Georges Racinet's avatar Georges Racinet 🦑
Browse files

Merge heptapod branch in release branch

--HG--
branch : heptapod-0-13
......@@ -15,7 +15,6 @@ def initialize(*args)
@file_path = File.join(ATTACHMENT_PATH, SecureRandom.hex, @file_name) if @file_name
@commit_message ||= "Upload attachment #{@file_name}"
@branch_name ||= wiki.default_branch
Rails.logger.info("testgr create att service branch_name=#{@branch_name}")
end
def create_commit!
......
......@@ -10,3 +10,12 @@
= open_raw_blob_button(blob)
= download_blob_button(blob)
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }<
= render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button
= ide_edit_button
- if current_user
= replace_blob_link
= delete_blob_link
= render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path
......@@ -8,3 +8,9 @@
#tree-holder.tree-holder
= render 'blob', blob: @blob
- if can_modify_blob?(@blob)
= render 'projects/blob/remove'
- title = "Replace #{@blob.name}"
= render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put
......@@ -100,10 +100,16 @@ def import_repo_config
end
def import_wiki
# gracinet: this isn't obvious, but BitbucketImport::ProjectCreator,
# the class responsible for repo creations will creates the wiki repo
# *only if* there's no wiki on the Bitbucket side.
# Therefore the condition below effectively avoids a useless and
# doomed import. This is unchanged in Heptapod at this point.
return if project.wiki.repository_exists?
wiki = WikiFormatter.new(project)
gitlab_shell.create_wiki_repository(project)
gitlab_shell.import_wiki_repository(project, wiki)
rescue StandardError => e
errors << { type: :wiki, errors: e.message }
......
......@@ -18,7 +18,7 @@ def full_path
end
def import_url
project.import_url.sub(/\.git\z/, ".git/wiki")
project.import_url.sub(/(\.git)?\z/, '\1/wiki')
end
end
end
......
......@@ -18,7 +18,6 @@ def user_can_download_code?
def check_change_access!
unless user_access.can_do_action?(:create_wiki)
Rails.logger.info("testgr failed test can_do_action create_wiki")
raise UnauthorizedError, ERROR_MESSAGES[:write_to_wiki]
end
......@@ -32,7 +31,6 @@ def check_change_access!
def user_can_push?
res = user_access.can_do_action?(:create_wiki) && !Gitlab::Database.read_only?
can_do = user_access.can_do_action?(:create_wiki)
Rails.logger.info("testgr HgAccessWirki user_can_push? -> #{res} can_do=#{can_do} user=#{user_access.user}")
res
end
......
......@@ -276,16 +276,26 @@ def hg_commit(path, username, message, env, add_remove = false)
hgsha_from_rev('.', path)
end
def has_tmp_workdir?(prefix)
!Dir.glob("#{@hgpath}tmp-#{prefix}-*").empty?
end
# Create an independent working directory
#
# this relies onto `hg share`, of which that's one of the main use
# cases.
#
# `update_rev`: revision to update the new working directoy to
# `prefix`: optional prefix used to check if a share is present
# for a given operation.
#
# returns path to the working directory
def hg_tmp_workdir(update_rev)
share_path = @hgpath + "tmp" + SecureRandom.hex
def hg_tmp_workdir(update_rev, prefix: nil)
share_path = @hgpath + "tmp"
unless prefix.nil?
share_path += "-" + prefix + '-'
end
share_path += SecureRandom.hex
begin
hg_exe = Gitlab.config.mercurial.bin_path
......@@ -491,7 +501,7 @@ def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:)
end_hgsha = hgsha_from_sha(end_sha)
username = hg_prepare_user_encoding(hg_env, author, message)
hg_tmp_workdir(nil) do |share_path|
hg_tmp_workdir(nil, prefix: "squash-#{squash_id}") do |share_path|
# TODO: gracinet not 100% sure we need a workdir, but I don't see
# an explicit "inmemory" option as there is for `hg rebase`
......@@ -543,7 +553,106 @@ def squash(user, squash_id, branch:, start_sha:, end_sha:, author:, message:)
# Actually at this point, reading the resulting changeset is not,
# but it doesn't matter much: there's no risk of repo corruption.
def squash_in_progress?(squash_id)
false
has_tmp_workdir?("squash-#{squash_id}")
end
# Perform a rebase, only for topics.
#
# We may be subject to the same race condition than explained in
# https://gitlab.com/gitlab-org/gitlab/-/issues/5966#note_150894306
# in short, there's a chance that the PostReceive gets executed
# before the resulting commit is written in the database.
# That's the kind of thing we will be able to fix once these
# methods are reimplemented in HGitaly (should be after Heptapod 1.0)
def rebase(user, rebase_id, branch:, branch_sha:,
remote_repository:, remote_branch:)
logprefix = "hg_rebase for #{@relative_path} of git #{branch_sha} "\
"(branch #{branch}) onto #{remote_branch}"
logger = Rails.logger
raise StandardError, "Rebasing between two different repos is not "\
"supported" unless remote_repository == self
topic = parse_gitlab_branch_for_hg(branch)[1]
raise HgError, "Only topics can be rebased" if topic.nil?
hg_exe = Gitlab.config.mercurial.bin_path
hg_env = hg_env_for_write(user)
# we need to notify GitLab: the merge request update is done at
# the receiving end of the post-receive hook
hg_env.delete('HEPTAPOD_SKIP_ALL_GITLAB_HOOKS')
# don't see an option to rebase with an obsmarker note
username = hg_prepare_user_encoding(hg_env, user, "")
end_hgsha = hgsha_from_sha(branch_sha)
raise HgError, "Could not find hg changeset for Git #{branch_sha}" if end_hgsha.nil?
# For consistency in corner cases (multiple heads) let's query the
# Git sha for the target branch, even though we could also parse
# and use Mercurial
dest_sha = find_branch(remote_branch)&.target
dest_hgsha = hgsha_from_sha(dest_sha)
raise HgError, "Could not find hg changeset for Git #{branch_sha}" if dest_hgsha.nil?
# revset insisting on using branch_sha because
# - the topic could actually span several named branches (we could also
# use the branch knowledge)
# - if branch_sha is not the branch/topic head, that means something
# is wrong. Instead of rebasing the whole, let's have the error
# we get by refusing instabilities
# topic naming rules make sure that we need no escaping
revset = "topic(#{topic}) and ::#{end_hgsha}"
rebase_hgsha =
hg_tmp_workdir(nil, prefix: "rebase-#{rebase_id}") do |share_path|
# TODO would be nice to experiment with in-memory rebase (wouldn't
# need a working dir) but not sure what the good use cases are.
# For instance, is a small rebase on a big repo much more efficient
# in memory or should that precisely be avoided?
# `allowunstable=no` protects us against all instabilities,
# in particular against orphaning dependent topics.
output, status = popen(
[hg_exe, 'rebase',
'-r', revset,
'-d', dest_hgsha,
'--tool', 'internal:merge3',
# extension activation should also end up in py-heptapod's
# required.hgrc, but let's make it work straight away
'--config', 'extensions.rebase=',
'--config', 'rebase.singletransaction=yes',
'--config', 'experimental.evolution.allowunstable=no',
'--config', 'ui.username=' + username,
],
share_path, hg_env)
if status != 0
raise HgError, "Could not rebase revset `#{revset}` "\
"onto #{dest_hgsha} Error: #{output}"
end
logger.info("#{logprefix} rebase done, finding successor")
output, status = popen(
[hg_exe, 'hpd-unique-successor', '-r', end_hgsha],
share_path, hg_env)
if status != 0
raise HgError, "Could not retrieve topic head after rebase "\
"(successor of #{end_hgsha})"
end
output.strip
end
hg_git_invalidate_maps!
rebase_sha = sha_from_hgsha(rebase_hgsha)
logger.info("#{logprefix} rebase successor changeset #{rebase_hgsha} "\
"(git sha #{rebase_sha})")
yield rebase_sha
rebase_sha
end
# first approx, but it'd be better to have a way to tell
# TODO use rebase_id in tmp workdir name (same for squash)
def rebase_in_progress?(rebase_id)
has_tmp_workdir?("rebase-#{rebase_id}")
end
# Pull a given revision from URL forcing topic on all new changesets
......@@ -591,6 +700,25 @@ def pull_force_topic(url, hgsha, topic)
node_branch.split(':')
end
def write_file(abspath, content)
if content.is_a?(UploadedFile)
# TODO mv would be more efficient, but it's unclear how much the
# UploadedFile would be then broken and whether it matters.
# (probably not much, the tempfile is an open file anyway).
# Another option if on same FS (case where mv is efficient)
# would be to make a hardlink. Playing safe for now.
FileUtils.cp(content.path, abspath)
else
# File.write uses text mode, which forces conversion to
# Encoding.external_encoding ('UTF-8') and gives an error.
# On the other hand, a file explicitely open in binary mode
# is happy to receive an UTF-8 string
File.open(abspath, "wb") do |f|
f.write(content)
end
end
end
# perform multiple write actions in the repository in a single commit
#
# typically multiple file writes or renames, followed by a commit
......@@ -669,7 +797,7 @@ def multi_action(user, branch_name:, message:, actions:,
logger.info(
"#{logprefix} Performing '#{action[:action]}' for #{file_path}")
case action[:action]
case action[:action].to_sym
when :create
if File.exists?(abspath)
raise ActionError, "A file with this name already exists"
......@@ -680,18 +808,12 @@ def multi_action(user, branch_name:, message:, actions:,
"already exists."
end
FileUtils.mkdir_p(parent_abspath)
# File.write uses text mode, which forces conversion to
# Encoding.external_encoding ('UTF-8') and gives an error.
# On the other hand, a file explicitely open in binary mode
# is happy to receive an UTF-8 string
File.open(abspath, "wb") do |f|
f.write(action[:content])
end
write_file(abspath, action[:content])
when :update
unless File.file?(abspath)
raise ActionError, "A file with this name doesn't exist"
end
File.write(abspath, action[:content])
write_file(abspath, action[:content])
when :create_dir
if File.file?(abspath)
raise ActionError, "A file with this name already exists "
......
......@@ -97,7 +97,17 @@ def create_repository(storage, disk_path, gl_project_path,
end
def import_wiki_repository(project, wiki_formatter)
import_repository(project.repository_storage, wiki_formatter.disk_path, wiki_formatter.import_url, project.wiki.full_path)
if project.mercurial?
import_hg_repository(project.repository_storage, wiki_formatter.disk_path, wiki_formatter.import_url)
# wikis really want to have a "master" branch. The generic hg repo
# importer won't do that because it doesn't send the GL_REPOSITORY
# environment var to the hg process.
# TODO refactor, but only after Heptapod 0.13.0 (too close to take
# any risk with the main import logic).
project.wiki.repository.create_branch("master", "branch/default")
else
import_repository(project.repository_storage, wiki_formatter.disk_path, wiki_formatter.import_url, project.wiki.full_path)
end
end
def import_project_repository(project)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment