Commit 6dc60935 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Update CE branch whenever EE is ahead of CE branch

This would happen if the corresponding CE branch is behind
CE master, and the CE merge base is ahead of CE branch.

When this happened, we could try to perform a merge to
CE merge base so that we keep up-to-date with the EE branch.

If there's a conflict, the author should resolve it from
the CE branch first.
parent b7aef3ae
......@@ -7,8 +7,8 @@ git_version
base = find_compare_base
current_numstat = updated_diff_numstat(base.ce_merge_base, base.ee_fetch_base)
updated_numstat = updated_diff_numstat(base.ce_updated_base, 'HEAD')
current_numstat = updated_diff_numstat(base.ce_base, base.ee_base)
updated_numstat = updated_diff_numstat(base.ce_head, base.ee_head)
offenses = updated_numstat.select do |file, updated_delta|
current_delta = current_numstat[file]
......
......@@ -14,7 +14,8 @@ module EESpecificCheck
'locale/gitlab.pot'
].freeze
CompareBase = Struct.new(:ce_merge_base, :ee_fetch_base, :ce_updated_base)
CompareBase = Struct.new(:ce_base, :ee_base, :ce_head, :ee_head)
GitStatus = Struct.new(:porcelain, :head)
module_function
......@@ -30,20 +31,16 @@ module EESpecificCheck
git_clean
ce_fetch_head = fetch_remote_ce_branch
ee_fetch_head = head_commit_sha
ce_fetch_base = run_git_command("merge-base canonical-ce/master #{ce_fetch_head}")
ce_merge_base = run_git_command("merge-base canonical-ce/master canonical-ee/master")
ee_fetch_base = run_git_command("merge-base canonical-ee/master HEAD")
ce_updated_base =
if ce_fetch_head.start_with?('canonical-ce') || # No specific CE branch
ce_fetch_base == ce_merge_base # Up-to-date, no rebase needed
ce_merge_base
else
checkout_and_rebase_ce_fetch_head_onto_ce_merge_base(
ce_fetch_head, ce_fetch_base, ce_merge_base)
end
ce_updated_head =
find_ce_compare_head(ce_fetch_head, ce_fetch_base, ce_merge_base)
CompareBase.new(ce_merge_base, ee_fetch_base, ce_updated_base)
CompareBase.new(
ce_merge_base, ee_fetch_base, ce_updated_head, ee_fetch_head)
end
def setup_canonical_remotes
......@@ -64,15 +61,6 @@ module EESpecificCheck
"#{remote_to_fetch}/#{branch_to_fetch}"
end
def checkout_and_rebase_ce_fetch_head_onto_ce_merge_base(
ce_fetch_head, ce_fetch_base, ce_merge_base)
# So that we could switch back
head = head_commit_sha
# Use detached HEAD so that we don't update HEAD
run_git_command("checkout -f #{ce_fetch_head}")
git_clean
# We rebase onto the commit which is the latest commit presented in both
# CE and EE, i.e. ce_merge_base, cutting off commits aren't merged into
# EE yet. Here's an example:
......@@ -109,13 +97,17 @@ module EESpecificCheck
# where ce_merge_base..ee_fetch_base is the update-to-date
# CE/EE difference and ce_fetch_head..HEAD is the changes we made in
# CE and EE branches.
run_git_command("rebase --onto #{ce_merge_base} #{ce_fetch_base}~1 #{ce_fetch_head}")
def checkout_and_rebase(new_base, old_base, target_head)
with_detached_head(target_head) do
run_git_command("rebase --onto #{new_base} #{old_base}~1 #{target_head}")
status = git_status
if status == ''
head_commit_sha
if status.porcelain == ''
status.head
else
run_git_command("rebase --abort")
say <<~MESSAGE
💥 Git status not clean! This shouldn't happen, but there are two
💥 known issues. One can be worked around, and the other can't.
......@@ -139,14 +131,23 @@ module EESpecificCheck
⚠️ Git status:
#{status}
#{status.porcelain}
MESSAGE
run_git_command("rebase --abort")
exit(255)
end
end
end
def with_detached_head(target_head)
# So that we could switch back
head = current_branch
# Use detached HEAD so that we don't update HEAD
run_git_command("checkout -f #{target_head}")
git_clean
yield
ensure # ensure would still run if we call exit, don't worry
# Make sure to switch back
run_git_command("checkout -f #{head}")
......@@ -158,7 +159,10 @@ module EESpecificCheck
end
def git_status
run_git_command("status --porcelain")
GitStatus.new(
run_git_command("status --porcelain"),
head_commit_sha
)
end
def git_clean
......@@ -171,6 +175,11 @@ module EESpecificCheck
run_git_command('clean -fd') if ENV['CI']
end
def git_ancestor?(ancestor, descendant)
run_git_command(
"merge-base --is-ancestor #{ancestor} #{descendant} && echo y") == 'y'
end
def remove_remotes
run_git_command(
"remote remove canonical-ee",
......@@ -202,6 +211,50 @@ module EESpecificCheck
end
end
def find_ce_compare_head(ce_fetch_head, ce_fetch_base, ce_merge_base)
if git_ancestor?(ce_merge_base, ce_fetch_base) # CE ahead of EE
find_backward_ce_head(ce_fetch_head, ce_fetch_base, ce_merge_base)
else # EE ahead of CE
find_forward_ce_head(ce_merge_base, ce_fetch_head)
end
end
def find_backward_ce_head(ce_fetch_head, ce_fetch_base, ce_merge_base)
if ce_fetch_head.start_with?('canonical-ce') || # No specific CE branch
ce_fetch_base == ce_merge_base # Up-to-date, no rebase needed
ce_merge_base
else
# Rebase CE to remove commits in CE haven't merged into EE
checkout_and_rebase(ce_merge_base, ce_fetch_base, ce_fetch_head)
end
end
def find_forward_ce_head(ce_merge_base, ce_fetch_head)
with_detached_head(ce_fetch_head) do
run_git_command("merge #{ce_merge_base}")
status = git_status
if status.porcelain == ''
status.head
else
run_git_command("merge --abort")
say <<~MESSAGE
💥 Git status not clean! This means there's a conflict in
💥 #{ce_fetch_head} with canonical-ce/master. Please resolve
💥 the conflict from CE master and retry this job.
⚠️ Git status:
#{status.porcelain}
MESSAGE
exit(254)
end
end
end
def ce_repo_url
@ce_repo_url ||= ENV.fetch('CI_REPOSITORY_URL', 'https://gitlab.com/gitlab-org/gitlab-ce.git').sub('gitlab-ee', 'gitlab-ce')
end
......
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