diff --git a/hgext3rd/heptapod/git.py b/hgext3rd/heptapod/git.py index 66ae6a19fb10b9698b141722f36593feb4cfd395_aGdleHQzcmQvaGVwdGFwb2QvZ2l0LnB5..6982b5005e9adbf56e209cfaa3858168ecb664b8_aGdleHQzcmQvaGVwdGFwb2QvZ2l0LnB5 100644 --- a/hgext3rd/heptapod/git.py +++ b/hgext3rd/heptapod/git.py @@ -92,7 +92,20 @@ return git_sha return handler.map_hg_get(git_sha) - return hg_sha(self.before), hg_sha(self.after) + before_hg_sha = hg_sha(self.before) + if before_hg_sha is None: + # before is not known to Git, this is a corruption as in the + # old heptapod/hg-git#3 and we must recover by creating the + # Git branch anyway. On the 'after' side, it would mean + # a severe inconsistency on our side + # TODO in a later version we should + # not convert at all, but we still rely as Git being + # a giant cache of previous state of GitLab branches. + before_hg_sha = ZERO_SHA + after_hg_sha = hg_sha(self.after) + if after_hg_sha is None: + raise KeyError(self.after) + return before_hg_sha, after_hg_sha return self.before, self.after @@ -622,8 +635,13 @@ if native and prune_reasons: prune_reasons = {gl_branch: reason.convert_native(self) for gl_branch, reason in prune_reasons.items()} - converted_changes = {ref: ch.export_for_hook(self, native=native) - for ref, ch in pycompat.iteritems(changes)} + try: + converted_changes = {ref: ch.export_for_hook(self, native=native) + for ref, ch in pycompat.iteritems(changes)} + except KeyError as exc: + ui.error(b"The after Git SHA %r has no Mercurial " + b"counterpart" % exc.args) + raise error.Abort("Inconsistency in internal conversion") # a verbose log line that will help with issues such as heptapod#278 ui.note(pycompat.sysbytes( diff --git a/hgext3rd/heptapod/tests/git/test_inner.py b/hgext3rd/heptapod/tests/git/test_inner.py index 66ae6a19fb10b9698b141722f36593feb4cfd395_aGdleHQzcmQvaGVwdGFwb2QvdGVzdHMvZ2l0L3Rlc3RfaW5uZXIucHk=..6982b5005e9adbf56e209cfaa3858168ecb664b8_aGdleHQzcmQvaGVwdGFwb2QvdGVzdHMvZ2l0L3Rlc3RfaW5uZXIucHk= 100644 --- a/hgext3rd/heptapod/tests/git/test_inner.py +++ b/hgext3rd/heptapod/tests/git/test_inner.py @@ -27,6 +27,7 @@ from ...git import ( git_branch_ref, HeptapodGitHandler, + GitRefChange as RefChange, # will probably be renamed ) from mercurial import pycompat from ..utils import common_config @@ -108,6 +109,31 @@ assert handler.heptapod_notify_gitlab('some-hook', {}, {}) is None +def test_native_unknown_git_shas(tmpdir): + wrapper = RepoWrapper.init(tmpdir.join('repo'), + config=dict(heptapod=dict(native="true"))) + wrapper.write_commit('foo', message='foo0') + handler = HeptapodGitHandler(wrapper.repo, wrapper.repo.ui) + hg_map = {b'known': b'cafe7654'} + handler.map_hg_get = lambda k: hg_map.get(k) + + records = [] + + def hook(*a): + records.append(a) + return 0, b'', b'' + + ch = RefChange(b'topic/default/zetop', before=b'unknown', after=b'known') + handler.heptapod_notify_gitlab(hook, {}, {b'topic/default/zetop': ch}) + + _prune, changes = records[0][0] + assert changes == {b'topic/default/zetop': (ZERO_SHA, b'cafe7654')} + + ch = RefChange(b'topic/default/zetop', before=b'known', after=b'unknown') + with pytest.raises(error.Abort): + handler.heptapod_notify_gitlab(hook, {}, {b'topic/default/zetop': ch}) + + def test_analyse_vanished_topic_lookup_error(tmpdir): wrapper = RepoWrapper.init(tmpdir.join('repo')) handler = HeptapodGitHandler(wrapper.repo, wrapper.repo.ui) diff --git a/hgext3rd/heptapod/tests/git/test_ref_change.py b/hgext3rd/heptapod/tests/git/test_ref_change.py new file mode 100644 index 0000000000000000000000000000000000000000..6982b5005e9adbf56e209cfaa3858168ecb664b8_aGdleHQzcmQvaGVwdGFwb2QvdGVzdHMvZ2l0L3Rlc3RfcmVmX2NoYW5nZS5weQ== --- /dev/null +++ b/hgext3rd/heptapod/tests/git/test_ref_change.py @@ -0,0 +1,38 @@ +# Copyright 2020 Georges Racinet <georges.racinet@octobus.net> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +# +# SPDX-License-Identifier: GPL-2.0-or-later +"""Tests for the GitRefChange object. + +(that may later be renamed as GitLabRefChange or other so that it doesn't +sound too weird once we are in full native mode) +""" +import pytest +from ...git import ( + GitRefChange as RefChange, # `as` for potential rename + ZERO_SHA, +) + + +class FakeHandler: + + def __init__(self, **git2hg): + self.git2hg = git2hg + + def map_hg_get(self, git_sha): + return self.git2hg.get(git_sha.decode()) + + +def test_export_for_hook_errors(): + handler = FakeHandler(known_git_sha=b'123d12fa45') + + ch = RefChange(b'branch/something', b'known_git_sha', b'the_unknown') + with pytest.raises(KeyError) as exc_info: + ch.export_for_hook(handler=handler, native=True) + assert exc_info.value.args[0] == b'the_unknown' + + ch = RefChange(b'branch/something', b'unknown', b'known_git_sha') + assert ch.export_for_hook(handler=handler, native=True) == ( + ZERO_SHA, b'123d12fa45')