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,89 +61,93 @@ 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)
# 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:
#
# * o: Relevant commits
# * x: Irrelevant commits
# * !: Commits we want to cut off from CE branch
#
# ^-> o CE branch (ce_fetch_head)
# / (ce_fetch_base)
# o -> o -> ! -> x CE master
# v (ce_merge_base)
# o -> o -> o -> x EE master
# \ (ee_fetch_base)
# v-> o EE branch
#
# We want to rebase above into this: (we only change the connection)
#
# -> - -> o CE branch (ce_fetch_head)
# / (ce_fetch_base)
# o -> o -> ! -> x CE master
# v (ce_merge_base)
# o -> o -> o -> x EE master
# \ (ee_fetch_base)
# v-> o EE branch
#
# Therefore we rebase onto ce_merge_base, which is based off CE master,
# for the CE branch (ce_fetch_head), effective remove the commit marked
# as ! in the graph for CE branch. We need to remove it because it's not
# merged into EE yet, therefore won't be available in the EE branch.
#
# After rebase is done, then we could compare against
# ce_merge_base..ee_fetch_base along with ce_fetch_head..HEAD (EE branch)
# 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.
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.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.
💥
💥 First please try to update your CE branch with CE master, and
💥 retry this job. You could find more information in this issue:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/issues/5960#note_72669536
💥
💥 It's possible, however, that that doesn't work out. In this case,
💥 please just disregard this job. You could find other information at:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/issues/6038
💥
💥 There's a work-in-progress fix at:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5719
💥
💥 If you would like to help, or have any questions, please
💥 contact @godfat
⚠️ Git status:
#{status.porcelain}
MESSAGE
exit(255)
end
end
end
def with_detached_head(target_head)
# So that we could switch back
head = head_commit_sha
head = current_branch
# Use detached HEAD so that we don't update HEAD
run_git_command("checkout -f #{ce_fetch_head}")
run_git_command("checkout -f #{target_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:
#
# * o: Relevant commits
# * x: Irrelevant commits
# * !: Commits we want to cut off from CE branch
#
# ^-> o CE branch (ce_fetch_head)
# / (ce_fetch_base)
# o -> o -> ! -> x CE master
# v (ce_merge_base)
# o -> o -> o -> x EE master
# \ (ee_fetch_base)
# v-> o EE branch
#
# We want to rebase above into this: (we only change the connection)
#
# -> - -> o CE branch (ce_fetch_head)
# / (ce_fetch_base)
# o -> o -> ! -> x CE master
# v (ce_merge_base)
# o -> o -> o -> x EE master
# \ (ee_fetch_base)
# v-> o EE branch
#
# Therefore we rebase onto ce_merge_base, which is based off CE master,
# for the CE branch (ce_fetch_head), effective remove the commit marked
# as ! in the graph for CE branch. We need to remove it because it's not
# merged into EE yet, therefore won't be available in the EE branch.
#
# After rebase is done, then we could compare against
# ce_merge_base..ee_fetch_base along with ce_fetch_head..HEAD (EE branch)
# 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}")
status = git_status
if status == ''
head_commit_sha
else
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.
💥
💥 First please try to update your CE branch with CE master, and
💥 retry this job. You could find more information in this issue:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/issues/5960#note_72669536
💥
💥 It's possible, however, that that doesn't work out. In this case,
💥 please just disregard this job. You could find other information at:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/issues/6038
💥
💥 There's a work-in-progress fix at:
💥
💥 https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5719
💥
💥 If you would like to help, or have any questions, please
💥 contact @godfat
⚠️ Git status:
#{status}
MESSAGE
run_git_command("rebase --abort")
exit(255)
end
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