git: use force fetch and update for target ref. This solves a case
when in PRs a target is force updated and is out of sync. Before we used a pull which --ff-only fails obviosly because two are out of sync. This change uses new logic that resets the target branch according to the source target branch allowing smooth merge simulation.
This commit is contained in:
parent
560fd7144f
commit
367a32a69a
3 changed files with 90 additions and 5 deletions
|
|
@ -747,6 +747,69 @@ class TestPullrequestsView(object):
|
|||
assert 'Pull request updated to' in response.body
|
||||
assert 'with 1 added, 1 removed commits.' in response.body
|
||||
|
||||
def test_update_target_revision_with_removal_of_1_commit_git(self, backend_git, csrf_token):
|
||||
backend = backend_git
|
||||
commits = [
|
||||
{'message': 'master-commit-1'},
|
||||
{'message': 'master-commit-2-change-1'},
|
||||
{'message': 'master-commit-3-change-2'},
|
||||
|
||||
{'message': 'feat-commit-1', 'parents': ['master-commit-1']},
|
||||
{'message': 'feat-commit-2'},
|
||||
]
|
||||
commit_ids = backend.create_master_repo(commits)
|
||||
target = backend.create_repo(heads=['master-commit-3-change-2'])
|
||||
source = backend.create_repo(heads=['feat-commit-2'])
|
||||
|
||||
# create pr from a in source to A in target
|
||||
pull_request = PullRequest()
|
||||
pull_request.source_repo = source
|
||||
# TODO: johbo: Make sure that we write the source ref this way!
|
||||
pull_request.source_ref = 'branch:{branch}:{commit_id}'.format(
|
||||
branch=backend.default_branch_name,
|
||||
commit_id=commit_ids['master-commit-3-change-2'])
|
||||
|
||||
pull_request.target_repo = target
|
||||
# TODO: johbo: Target ref should be branch based, since tip can jump
|
||||
# from branch to branch
|
||||
pull_request.target_ref = 'branch:{branch}:{commit_id}'.format(
|
||||
branch=backend.default_branch_name,
|
||||
commit_id=commit_ids['feat-commit-2'])
|
||||
|
||||
pull_request.revisions = [
|
||||
commit_ids['feat-commit-1'],
|
||||
commit_ids['feat-commit-2']
|
||||
]
|
||||
pull_request.title = u"Test"
|
||||
pull_request.description = u"Description"
|
||||
pull_request.author = UserModel().get_by_username(
|
||||
TEST_USER_ADMIN_LOGIN)
|
||||
Session().add(pull_request)
|
||||
Session().commit()
|
||||
pull_request_id = pull_request.pull_request_id
|
||||
|
||||
# PR is created, now we simulate a force-push into target,
|
||||
# that drops a 2 last commits
|
||||
vcsrepo = target.scm_instance()
|
||||
vcsrepo.config.clear_section('hooks')
|
||||
vcsrepo.run_git_command(['reset', '--soft', 'HEAD~2'])
|
||||
|
||||
# update PR
|
||||
self.app.post(
|
||||
route_path('pullrequest_update',
|
||||
repo_name=target.repo_name,
|
||||
pull_request_id=pull_request_id),
|
||||
params={'update_commits': 'true',
|
||||
'csrf_token': csrf_token},
|
||||
status=200)
|
||||
|
||||
response = self.app.get(route_path(
|
||||
'pullrequest_new',
|
||||
repo_name=target.repo_name))
|
||||
assert response.status_int == 200
|
||||
response.mustcontain('Pull request updated to')
|
||||
response.mustcontain('with 0 added, 0 removed commits.')
|
||||
|
||||
def test_update_of_ancestor_reference(self, backend, csrf_token):
|
||||
commits = [
|
||||
{'message': 'ancestor'},
|
||||
|
|
|
|||
|
|
@ -757,7 +757,7 @@ class GitRepository(BaseRepository):
|
|||
cmd = ['fetch', self.path, source_branch]
|
||||
self.run_git_command(cmd, fail_on_stderr=False)
|
||||
|
||||
def _local_fetch(self, repository_path, branch_name):
|
||||
def _local_fetch(self, repository_path, branch_name, use_origin=False):
|
||||
"""
|
||||
Fetch a branch from a local repository.
|
||||
"""
|
||||
|
|
@ -765,7 +765,17 @@ class GitRepository(BaseRepository):
|
|||
if repository_path == self.path:
|
||||
raise ValueError('Cannot fetch from the same repository')
|
||||
|
||||
cmd = ['fetch', '--no-tags', repository_path, branch_name]
|
||||
if use_origin:
|
||||
branch_name = '+{branch}:refs/heads/{branch}'.format(
|
||||
branch=branch_name)
|
||||
|
||||
cmd = ['fetch', '--no-tags', '--update-head-ok',
|
||||
repository_path, branch_name]
|
||||
self.run_git_command(cmd, fail_on_stderr=False)
|
||||
|
||||
def _local_reset(self, branch_name):
|
||||
branch_name = '{}'.format(branch_name)
|
||||
cmd = ['reset', '--hard', branch_name]
|
||||
self.run_git_command(cmd, fail_on_stderr=False)
|
||||
|
||||
def _last_fetch_heads(self):
|
||||
|
|
@ -840,7 +850,7 @@ class GitRepository(BaseRepository):
|
|||
'merge', '--no-ff', '-m', safe_str(merge_message)]
|
||||
cmd.extend(heads)
|
||||
try:
|
||||
self.run_git_command(cmd, fail_on_stderr=False)
|
||||
output = self.run_git_command(cmd, fail_on_stderr=False)
|
||||
except RepositoryError:
|
||||
# Cleanup any merge leftovers
|
||||
self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
|
||||
|
|
@ -897,6 +907,8 @@ class GitRepository(BaseRepository):
|
|||
merger_name, merger_email, dry_run=False,
|
||||
use_rebase=False, close_branch=False):
|
||||
if target_ref.commit_id != self.branches[target_ref.name]:
|
||||
log.warning('Target ref %s commit mismatch %s vs %s', target_ref,
|
||||
target_ref.commit_id, self.branches[target_ref.name])
|
||||
return MergeResponse(
|
||||
False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
|
||||
|
||||
|
|
@ -907,20 +919,29 @@ class GitRepository(BaseRepository):
|
|||
if shadow_repo.get_remote_ref(source_ref.name):
|
||||
shadow_repo._checkout(source_ref.name, force=True)
|
||||
|
||||
# checkout target
|
||||
# checkout target, and fetch changes
|
||||
shadow_repo._checkout(target_ref.name, force=True)
|
||||
shadow_repo._local_pull(self.path, target_ref.name)
|
||||
|
||||
# fetch/reset pull the target, in case it is changed
|
||||
# this handles even force changes
|
||||
shadow_repo._local_fetch(self.path, target_ref.name, use_origin=True)
|
||||
shadow_repo._local_reset(target_ref.name)
|
||||
|
||||
# Need to reload repo to invalidate the cache, or otherwise we cannot
|
||||
# retrieve the last target commit.
|
||||
shadow_repo = GitRepository(shadow_repository_path)
|
||||
if target_ref.commit_id != shadow_repo.branches[target_ref.name]:
|
||||
log.warning('Shadow Target ref %s commit mismatch %s vs %s',
|
||||
target_ref, target_ref.commit_id,
|
||||
shadow_repo.branches[target_ref.name])
|
||||
return MergeResponse(
|
||||
False, False, None, MergeFailureReason.TARGET_IS_NOT_HEAD)
|
||||
|
||||
# calculate new branch
|
||||
pr_branch = shadow_repo._get_new_pr_branch(
|
||||
source_ref.name, target_ref.name)
|
||||
log.debug('using pull-request merge branch: `%s`', pr_branch)
|
||||
# checkout to temp branch, and fetch changes
|
||||
shadow_repo._checkout(pr_branch, create=True)
|
||||
try:
|
||||
shadow_repo._local_fetch(source_repo.path, source_ref.name)
|
||||
|
|
|
|||
|
|
@ -417,6 +417,7 @@
|
|||
<strong>${_('Missing commits')}:</strong>
|
||||
${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
|
||||
${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
|
||||
${_('Consider doing a {force_refresh_url} in case you think this is an error.').format(force_refresh_url=h.link_to('force refresh', h.current_route_path(request, force_refresh='1')))|n}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue