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 ...@@ -7,8 +7,8 @@ git_version
base = find_compare_base base = find_compare_base
current_numstat = updated_diff_numstat(base.ce_merge_base, base.ee_fetch_base) current_numstat = updated_diff_numstat(base.ce_base, base.ee_base)
updated_numstat = updated_diff_numstat(base.ce_updated_base, 'HEAD') updated_numstat = updated_diff_numstat(base.ce_head, base.ee_head)
offenses = updated_numstat.select do |file, updated_delta| offenses = updated_numstat.select do |file, updated_delta|
current_delta = current_numstat[file] current_delta = current_numstat[file]
......
...@@ -14,7 +14,8 @@ module EESpecificCheck ...@@ -14,7 +14,8 @@ module EESpecificCheck
'locale/gitlab.pot' 'locale/gitlab.pot'
].freeze ].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 module_function
...@@ -30,20 +31,16 @@ module EESpecificCheck ...@@ -30,20 +31,16 @@ module EESpecificCheck
git_clean git_clean
ce_fetch_head = fetch_remote_ce_branch 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_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") 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") ee_fetch_base = run_git_command("merge-base canonical-ee/master HEAD")
ce_updated_base = ce_updated_head =
if ce_fetch_head.start_with?('canonical-ce') || # No specific CE branch find_ce_compare_head(ce_fetch_head, ce_fetch_base, ce_merge_base)
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
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 end
def setup_canonical_remotes def setup_canonical_remotes
...@@ -64,15 +61,6 @@ module EESpecificCheck ...@@ -64,15 +61,6 @@ module EESpecificCheck
"#{remote_to_fetch}/#{branch_to_fetch}" "#{remote_to_fetch}/#{branch_to_fetch}"
end 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 # 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 # CE and EE, i.e. ce_merge_base, cutting off commits aren't merged into
# EE yet. Here's an example: # EE yet. Here's an example:
...@@ -109,13 +97,17 @@ module EESpecificCheck ...@@ -109,13 +97,17 @@ module EESpecificCheck
# where ce_merge_base..ee_fetch_base is the update-to-date # 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/EE difference and ce_fetch_head..HEAD is the changes we made in
# CE and EE branches. # 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 status = git_status
if status == '' if status.porcelain == ''
head_commit_sha status.head
else else
run_git_command("rebase --abort")
say <<~MESSAGE say <<~MESSAGE
💥 Git status not clean! This shouldn't happen, but there are two 💥 Git status not clean! This shouldn't happen, but there are two
💥 known issues. One can be worked around, and the other can't. 💥 known issues. One can be worked around, and the other can't.
...@@ -139,14 +131,23 @@ module EESpecificCheck ...@@ -139,14 +131,23 @@ module EESpecificCheck
⚠️ Git status: ⚠️ Git status:
#{status} #{status.porcelain}
MESSAGE MESSAGE
run_git_command("rebase --abort")
exit(255) exit(255)
end 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 ensure # ensure would still run if we call exit, don't worry
# Make sure to switch back # Make sure to switch back
run_git_command("checkout -f #{head}") run_git_command("checkout -f #{head}")
...@@ -158,7 +159,10 @@ module EESpecificCheck ...@@ -158,7 +159,10 @@ module EESpecificCheck
end end
def git_status def git_status
run_git_command("status --porcelain") GitStatus.new(
run_git_command("status --porcelain"),
head_commit_sha
)
end end
def git_clean def git_clean
...@@ -171,6 +175,11 @@ module EESpecificCheck ...@@ -171,6 +175,11 @@ module EESpecificCheck
run_git_command('clean -fd') if ENV['CI'] run_git_command('clean -fd') if ENV['CI']
end end
def git_ancestor?(ancestor, descendant)
run_git_command(
"merge-base --is-ancestor #{ancestor} #{descendant} && echo y") == 'y'
end
def remove_remotes def remove_remotes
run_git_command( run_git_command(
"remote remove canonical-ee", "remote remove canonical-ee",
...@@ -202,6 +211,50 @@ module EESpecificCheck ...@@ -202,6 +211,50 @@ module EESpecificCheck
end end
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 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') @ce_repo_url ||= ENV.fetch('CI_REPOSITORY_URL', 'https://gitlab.com/gitlab-org/gitlab-ce.git').sub('gitlab-ee', 'gitlab-ce')
end 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