Skip to content
Snippets Groups Projects
Commit 1e598e29 authored by Georges Racinet's avatar Georges Racinet
Browse files

Obsolescence: forbid making a bookmark obsolete

In general, making a bookmark obsolete will resolve it:
`hg amend` will move it to the successor, `hg prune` will
move it to the parent. We found one corner case to make
the target of a bookmark obsolete (`hg prune` when there is
no parent). But there may be other ones (`hg split` maybe?)
parent f4894147
No related branches found
No related tags found
2 merge requests!45Validate obsolescence,!44config: add option to never exchange bookmarks
......@@ -248,6 +248,33 @@
gitlab_branch))
to_prune[gitlab_branch] = AllHeadsBookmarked()
def validate_obsolescence(self, txn):
"""Abort if the given transaction creates unwanted obsolescence.
Most GitLab refs must not be to obsolete changesets, but we don't
want to create blocking situations for repos with long history
where this may already be the case.
See heptapod#393
We are at the end of the transaction, hence we can assume that
consequences of new obsmarkers have already been applied (e.g, moving
a bookmark if possible)
"""
unfi = self.repo.unfiltered()
for new_obsmarker in txn.changes[b'obsmarkers']:
obsolete_ctx = unfi[new_obsmarker[0]]
bookmarks = obsolete_ctx.bookmarks()
if bookmarks:
hint = _(b"If this isn't a mistake, you may need to also "
b"update the bookmark(s).")
msg = _(b"Refusing these changes, as they would make "
b"bookmark(s) %s point to the "
b"obsolete changeset %s") % (
util.format_bytes_list(bookmarks),
obsolete_ctx.hex())
raise error.Abort(msg, hint=hint)
def get_exportable(self):
"""Heptapod version, including named branches and topics.
......@@ -271,9 +298,15 @@
b'hg-git.bookmarks-on-named-branches')
allow_multiple_heads = ui.configbool(b'heptapod',
b'allow-multiple-heads')
bm_changes = txn.changes.get(b'bookmarks') if txn is not None else None
deleted_bms = self.heptapod_gate_bookmarks(
repo, allow_bookmarks, bm_changes)
if txn is None:
deleted_bms = ()
else:
bm_changes = txn.changes.get(b'bookmarks')
deleted_bms = self.heptapod_gate_bookmarks(
repo, allow_bookmarks, bm_changes)
# after general bookmark gating in order not to mask the
# more common message that bookmarks aren't allowed anyhow.
self.validate_obsolescence(txn)
to_prune.update((bm, BookmarkRemoved(prev_git_sha))
for (bm, prev_git_sha) in deleted_bms)
......
......@@ -25,6 +25,7 @@
TopicPublished,
WildHeadResolved,
)
from mercurial_testhelpers.repo_wrapper import NULL_ID
from heptapod.testhelpers import (
RepoWrapper,
)
......@@ -431,6 +432,34 @@
assert notifs == [('pre-receive', changes), ('post-receive', changes)]
def test_bookmarks_obsolete(tmpdir, monkeypatch):
notifs = []
patch_gitlab_hooks(monkeypatch, notifs)
repo_path = tmpdir.join('repo.hg')
config = common_config()
config['extensions']['hggit'] = ''
config['phases'] = dict(publish=False)
wrapper = RepoWrapper.init(repo_path, config=config)
set_allow_bookmarks(wrapper, True)
git_repo = GitRepo.init(tmpdir.join('repo.git'))
# getting gitlab-mirror to work in-transaction
draft1 = wrapper.commit_file('foo', message='Initial draft')
wrapper.command('bookmark', b'zebook', rev=draft1.hex(), inactive=True)
wrapper.command('gitlab-mirror')
# just checking our assumptions.
assert git_repo.branch_titles() == {b'zebook': b'Initial draft'}
# let's get in-transaction
activate_mirror(wrapper)
draft2 = wrapper.commit_file('foo', message='amended', parent=NULL_ID)
with pytest.raises(error.Abort) as exc_info:
wrapper.prune(draft1.hex(), successors=[draft2.hex()])
assert re.search(br'bookmark.*zebook.*obsolete', exc_info.value.args[0])
def test_bookmarks_dont_mask_default_branch(tmpdir, monkeypatch):
notifs = []
patch_gitlab_hooks(monkeypatch, notifs)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment