Esempio n. 1
0
def prompt_for_confirmation(context: Context, fail_title: str, message: str,
                            prompt: str):
    result = Result()

    if context.batch:
        result.value = context.assume_yes
        if not result.value:
            sys.stdout.write(prompt + ' -' + os.linesep)
            result.fail(const.EX_ABORTED, fail_title,
                        _("Operation aborted in batch mode."))
    else:
        if message is not None:
            cli.warn(message)
        sys.stderr.flush()
        sys.stdout.flush()

        if context.assume_yes:
            sys.stdout.write(prompt + ' y' + os.linesep)
            result.value = True
        else:
            result.value = cli.query_yes_no(sys.stdout, prompt, "no")

        if result.value is not True:
            result.error(const.EX_ABORTED_BY_USER, fail_title,
                         _("Operation aborted."), False)

    return result
Esempio n. 2
0
def select_ref(result_out: Result, branch_info: BranchInfo, selection: BranchSelection) \
        -> [repotools.Ref, const.BranchClass]:
    if branch_info.local is not None and len(
            branch_info.local) and branch_info.upstream is not None:
        if branch_info.local_class[0] != branch_info.upstream_class:
            result_out.error(
                os.EX_DATAERR,
                _("Local and upstream branch have a mismatching branch class."
                  ), None)
        if not branch_info.upstream.short_name.endswith(
                '/' + branch_info.local[0].short_name):
            result_out.error(
                os.EX_DATAERR,
                _("Local and upstream branch have a mismatching short name."),
                None)

    candidate = None
    candidate_class = None
    if selection == BranchSelection.BRANCH_PREFER_LOCAL:
        candidate = branch_info.local[0] or branch_info.upstream
        candidate_class = branch_info.local_class[
            0] or branch_info.upstream_class
    elif selection == BranchSelection.BRANCH_LOCAL_ONLY:
        candidate = branch_info.local[0]
        candidate_class = branch_info.local_class[0]
    elif selection == BranchSelection.BRANCH_PREFER_REMOTE:
        candidate = branch_info.upstream or branch_info.local[0]
        candidate_class = branch_info.upstream_class or branch_info.local_class[
            0]
    elif selection == BranchSelection.BRANCH_REMOTE_ONLY:
        candidate = branch_info.upstream
        candidate_class = branch_info.upstream_class
    return candidate, candidate_class
Esempio n. 3
0
def evaluate_numeric_increment(result: Result, field_name: str, reset: bool, reset_val: int, strict: bool, a: int,
                               b: int):
    """
    :rtype: bool
    """

    delta = b - a

    if reset:
        if b != reset_val:
            result.error(os.EX_USAGE,
                         _("Version change leaves a gap without a semantic meaning."),
                         _("The field {field_name} must be reset to {reset_val}.")
                         .format(field_name=repr(field_name)
                                 , reset_val=reset_val)
                         )
    else:
        if delta > 1:
            result.error(os.EX_USAGE if strict else os.EX_OK,
                         _("Version change leaves a gap without a semantic meaning."),
                         _("The field {field_name} must not be incremented by more than one.")
                         .format(field_name=repr(field_name))
                         )

    return reset or b > a
Esempio n. 4
0
def evaluate_prerelease_increment(result: Result, field_name: str, index: int, reset: bool, reset_val: int or str,
                                  strict: bool,
                                  a: int or str, b: int or str,
                                  keywords: list):
    """
    :rtype: bool
    """

    delta = cmp_alnum_token(b, a, keywords)
    requires_reset = False

    if delta > 0:
        requires_reset = True

    if reset:
        if b != reset_val:
            result.error(os.EX_USAGE if strict else os.EX_OK,
                         _("Version change leaves a gap without a semantic meaning."),
                         _("The field {field_name} must be reset to {reset_val}.")
                         .format(field_name=repr(field_name + '[' + str(index) + ']'),
                                 reset_val=reset_val)
                         )
    else:
        if delta > 1:
            result.error(os.EX_USAGE if strict else os.EX_OK,
                         _("Version change leaves a gap without a semantic meaning."),
                         _("The field {field_name} must not be incremented by more than one.")
                         .format(field_name=repr(field_name + '[' + str(index) + ']'))
                         )

    return reset or requires_reset
Esempio n. 5
0
class AbstractContext(object):
    result: Result = None

    def __init__(self):
        self.result = Result()

    def warn(self, message, reason):
        self.result.warn(message, reason)

    def error(self, exit_code, message, reason, throw: bool = False):
        self.result.error(exit_code, message, reason, throw)

    def fail(self, exit_code, message, reason):
        self.result.fail(exit_code, message, reason)

    def add_subresult(self, subresult):
        self.result.add_subresult(subresult)

    def has_errors(self):
        return self.result.has_errors()

    def abort_on_error(self):
        return self.result.abort_on_error()

    def abort(self):
        return self.result.abort()
def version_bump_to_release(version_config: VersionConfig,
                            version: Optional[str], global_seq: Optional[int]):
    result = Result()
    version_info = semver.parse_version_info(
        version) if version is not None else semver.parse_version_info("0.0.0")

    if version_config.versioning_scheme == VersioningScheme.SEMVER_WITH_SEQ:
        result.error(
            os.EX_USAGE,
            _("Failed to increment version to release: {version}.").format(
                version=repr(version)),
            _("Sequential versions cannot be release versions."))
        return result

    if not version_info.prerelease:
        result.error(
            os.EX_DATAERR,
            _("Failed to increment version to release: {version}.").format(
                version=repr(version)),
            _("Only pre-release versions can be incremented to a release version."
              ))

    if not result.has_errors():
        result.value = semver.format_version(version_info.major,
                                             version_info.minor,
                                             version_info.patch, None, None)
    return result
Esempio n. 7
0
def fail(exit_code, *message):
    # TODO remove
    for line in message:
        eprint(line)
    if exit_code == os.EX_OK:
        eprint("internal error")
        exit_code = os.EX_SOFTWARE
    result = Result()
    result.error(exit_code, os.linesep.join(message), None)
    raise GitFlowException(result)
def version_bump_prerelease(version_config: VersionConfig,
                            version: Optional[str], global_seq: Optional[int]):
    result = Result()
    version_info = semver.parse_version_info(
        version) if version is not None else semver.parse_version_info("0.0.0")

    if version_info.prerelease:
        prerelease_version_elements = version_info.prerelease.split(".")
        if len(prerelease_version_elements
               ) > 0 and prerelease_version_elements[0].upper() == "SNAPSHOT":
            if len(prerelease_version_elements) == 1:
                result.error(
                    os.EX_DATAERR,
                    _("The pre-release increment has been skipped."),
                    _("In order to retain Maven compatibility, "
                      "the pre-release component of snapshot versions must not be versioned."
                      ))
            else:
                result.error(
                    os.EX_DATAERR,
                    _("Failed to increment the pre-release component of version {version}."
                      ).format(version=repr(version)),
                    _("Snapshot versions must not have a pre-release version.")
                )
            result.value = version
        elif len(prerelease_version_elements) == 1:
            if version_config.versioning_scheme != VersioningScheme.SEMVER_WITH_SEQ:
                result.error(
                    os.EX_DATAERR,
                    _("Failed to increment the pre-release component of version {version}."
                      ).format(version=repr(version)),
                    _("The qualifier {qualifier} must already be versioned.").
                    format(qualifier=repr(prerelease_version_elements[0])))
        result.value = semver.bump_prerelease(version)
    else:
        result.error(
            os.EX_DATAERR,
            _("Failed to increment the pre-release component of version {version}."
              ).format(version=repr(version)),
            _("Pre-release increments cannot be performed on release versions."
              ))

    if result.has_errors():
        result.value = None
    elif result.value is not None and not semver.compare(
            result.value, version) > 0:
        result.value = None

    if not result.value:
        result.error(
            os.EX_SOFTWARE,
            _("Failed to increment the pre-release of version {version} for unknown reasons."
              ).format(version=repr(version)), None)
    return result
Esempio n. 9
0
def evaluate_version_increment(a: Version, b: Version, strict: bool, prerelase_keywords_list: list = None):
    result = Result()

    initial_version = Version()
    initial_version.major = 1
    initial_version.minor = 0
    initial_version.patch = 0
    if prerelase_keywords_list is not None and len(prerelase_keywords_list):
        initial_version.prerelease = list()
        for index, token_config in enumerate(prerelase_keywords_list):
            if token_config is not None:
                if isinstance(token_config, list) and len(token_config):
                    initial_version.prerelease.append(token_config[0])
                elif isinstance(token_config, int):
                    initial_version.prerelease.append(token_config)
                else:
                    raise ValueError()
            else:
                initial_version.prerelease.append(0)

    reset = False
    reset = evaluate_numeric_increment(result, 'major', reset, initial_version.major, strict, a.major, b.major)
    reset = evaluate_numeric_increment(result, 'minor', reset, initial_version.minor, strict, a.minor, b.minor)
    reset = evaluate_numeric_increment(result, 'patch', reset, initial_version.patch, strict, a.patch, b.patch)

    # check pre-release convention
    index = 0
    for sub_a, sub_b in itertools.zip_longest(a.prerelease or [],
                                              b.prerelease or []):
        keywords = prerelase_keywords_list[index] \
            if prerelase_keywords_list is not None \
               and index < len(prerelase_keywords_list) \
            else None
        reset = evaluate_prerelease_increment(result, "prerelease", index,
                                              reset, initial_version.prerelease[index]
                                              if initial_version.prerelease is not None
                                                 and index < len(initial_version.prerelease)
                                              else 0,
                                              strict, sub_a, sub_b, keywords)
        index += 1

    if result.has_errors():
        result.error(os.EX_USAGE if strict else os.EX_OK,
                     _("Version increment is flawed."),
                     _("A version increment from {version_a} to {version_b} is inconsistent.")
                     .format(version_a=repr(format_version(a)), version_b=repr(format_version(b)))
                     )

    return result
Esempio n. 10
0
def version_bump_qualifier(version_config: VersionConfig,
                           version: Optional[str], global_seq: Optional[int]):
    result = Result()
    version_info = semver.parse_version_info(
        version) if version is not None else semver.parse_version_info("0.0.0")

    new_qualifier = None

    if not version_config.qualifiers:
        result.error(
            os.EX_USAGE,
            _("Failed to increment the pre-release qualifier of version {version}."
              ).format(version=repr(version)),
            _("The version scheme does not contain qualifiers"))
        return result

    if version_info.prerelease:
        prerelease_version_elements = version_info.prerelease.split(".")
        qualifier = prerelease_version_elements[0]
        qualifier_index = version_config.qualifiers.index(
            qualifier) if qualifier in version_config.qualifiers else -1
        if qualifier_index < 0:
            result.error(
                os.EX_DATAERR,
                _("Failed to increment the pre-release qualifier of version {version}."
                  ).format(version=repr(version)),
                _("The current qualifier is invalid: {qualifier}").format(
                    qualifier=repr(qualifier)))
        else:
            qualifier_index += 1
            if qualifier_index < len(version_config.qualifiers):
                new_qualifier = version_config.qualifiers[qualifier_index]
            else:
                result.error(
                    os.EX_DATAERR,
                    _("Failed to increment the pre-release qualifier {qualifier} of version {version}."
                      ).format(qualifier=qualifier, version=repr(version)),
                    _("There are no further qualifiers with higher precedence, configured qualifiers are:\n"
                      "{listing}\n"
                      "The sub command 'bump-to-release' may be used for a final bump."
                      ).format(listing='\n'.join(
                          ' - ' + repr(qualifier)
                          for qualifier in version_config.qualifiers)))
    else:
        result.error(
            os.EX_DATAERR,
            _("Failed to increment the pre-release qualifier of version {version}."
              ).format(version=version),
            _("Pre-release increments cannot be performed on release versions."
              ))

    if not result.has_errors() and new_qualifier is not None:
        result.value = semver.format_version(version_info.major,
                                             version_info.minor,
                                             version_info.patch,
                                             new_qualifier + ".1", None)
    return result
Esempio n. 11
0
def download_file(source_uri: str, dest_file: str, hash_hex: str):
    from urllib import request
    import hashlib

    result = Result()

    hash = bytes.fromhex(hash_hex)

    download = False

    if not os.path.exists(dest_file):
        cli.print("file does not exist: " + dest_file)
        download = True
    elif hash_file(hashlib.sha256(), dest_file) != hash:
        cli.print("file hash does not match: " + dest_file)
        download = True
    else:
        cli.print("keeping file: " + dest_file + ", sha256 matched: " +
                  hash_hex)

    if download:
        cli.print("downloading: " + source_uri + " to " + dest_file)
        request.urlretrieve(url=str(source_uri), filename=dest_file + "~")
        filesystem.replace_file(dest_file + "~", dest_file)

        if hash is not None:
            actual_hash = hash_file(hashlib.sha256(), dest_file)
            if actual_hash != hash:
                result.error(
                    os.EX_IOERR, _("File verification failed."),
                    _("The file {file} is expected to hash to {expected_hash},\n"
                      "The actual hash is: {actual_hash}").format(
                          file=repr(dest_file),
                          expected_hash=repr(hash_hex),
                          actual_hash=repr(actual_hash.hex()),
                      ))

    if not result.has_errors():
        result.value = dest_file

    return result
Esempio n. 12
0
def validate_version(config: VersionConfig, version_string):
    result = Result()

    try:
        version_info = semver.parse_version_info(version_string)
        if version_info.prerelease is not None:
            if version_info.prerelease is not None and not re.match(r'[a-zA-Z][a-zA-Z0-9]*\.\d',
                                                                    version_info.prerelease):
                result.error(os.EX_DATAERR,
                             "Invalid version format.",
                             "The pre-release component must contain a type name with a version number.\n"
                             "The required version format is:\n"
                             + const.TEXT_VERSION_STRING_FORMAT)
            prerelease_version_elements = version_info.prerelease.split(".")
            prerelease_type = prerelease_version_elements[0]
            prerelease_version = prerelease_version_elements[1]

            if config.qualifiers is not None and prerelease_type not in config.qualifiers:
                result.error(os.EX_DATAERR,
                             "Invalid version.",
                             "The pre-release type \"" + prerelease_type + "\" is invalid, must be one of: "
                             + ','.join(config.qualifiers) + ".\n"
                             + "Configuration property: " + const.CONFIG_VERSION_TYPES)
        result.value = version_string
    except ValueError:
        result.error(os.EX_DATAERR,
                     "Failed to parse the version.",
                     "The required version format is:\n"
                     + const.TEXT_VERSION_STRING_FORMAT)

    return result
Esempio n. 13
0
def version_bump_patch(version_config: VersionConfig, version: Optional[str],
                       global_seq: Optional[int]):
    result = Result()

    try:
        global_seq = filter_sequence_number(version_config, version,
                                            global_seq)
    except ValueError as e:
        result.error(os.EX_DATAERR, "version increment failed", str(e))

    if not result.has_errors():
        version_info = semver.parse_version_info(
            semver.bump_patch(version)
        ) if version is not None else semver.parse_version_info("0.0.0")
        pre_release = True

        result.value = semver.format_version(
            version_info.major, version_info.minor, version_info.patch,
            (version_config.qualifiers[0] +
             ".1" if pre_release else None) if version_config.versioning_scheme
            != VersioningScheme.SEMVER_WITH_SEQ else global_seq + 1, None)
    return result
Esempio n. 14
0
def clone_repository(context: Context, branch: str) -> Result:
    """
    :rtype: Result
    """
    result = Result()

    remote = repotools.git_get_remote(context.repo, context.config.remote_name)
    if remote is None:
        result.fail(
            os.EX_DATAERR, _("Failed to clone repo."),
            _("The remote {remote} does not exist.").format(
                remote=repr(context.config.remote_name)))

    tempdir_path = tempfile.mkdtemp(prefix=os.path.basename(context.repo.dir) +
                                    ".gitflow-clone.")
    try:
        if os.path.exists(tempdir_path):
            os.chmod(path=tempdir_path, mode=0o700)
            if os.path.isdir(tempdir_path):
                if os.listdir(tempdir_path):
                    result.fail(
                        os.EX_DATAERR, _("Failed to clone repo."),
                        _("Directory is not empty: {path}").format(
                            path=tempdir_path))
            else:
                result.fail(
                    os.EX_DATAERR, _("Failed to clone repo."),
                    _("File is not a directory: {path}").format(
                        path=tempdir_path))
        else:
            result.fail(
                os.EX_DATAERR, _("Failed to clone repo."),
                _("File does not exist: {path}").format(path=tempdir_path))

        if context.config.push_to_local:
            returncode, out, err = repotools.git_raw(git=context.repo.git,
                                                     args=[
                                                         'clone', '--branch',
                                                         branch, '--shared',
                                                         context.repo.dir,
                                                         tempdir_path
                                                     ],
                                                     verbose=context.verbose)
        else:
            returncode, out, err = repotools.git_raw(git=context.repo.git,
                                                     args=[
                                                         'clone', '--branch',
                                                         branch, '--reference',
                                                         context.repo.dir,
                                                         remote.url,
                                                         tempdir_path
                                                     ],
                                                     verbose=context.verbose)

        if returncode != os.EX_OK:
            result.error(os.EX_DATAERR, _("Failed to clone the repository."),
                         _("An unexpected error occurred."))
    except:
        result.error(os.EX_DATAERR, _("Failed to clone the repository."),
                     _("An unexpected error occurred."))
    finally:
        context.add_subresult(result)

    if not result.has_errors():
        repo = RepoContext()
        repo.git = context.repo.git
        repo.dir = tempdir_path
        repo.verbose = context.repo.verbose
        result.value = repo
    else:
        shutil.rmtree(path=tempdir_path)

    return result
Esempio n. 15
0
def create_version_branch(command_context: CommandContext,
                          operation: Callable[[VersionConfig, Optional[str], Optional[int]], Result]) -> Result:
    result = Result()
    context: Context = command_context.context

    if command_context.selected_ref.name not in [
        repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, context.config.release_branch_base),
        repotools.create_ref_name(const.REMOTES_PREFIX,
                                  context.config.remote_name,
                                  context.config.release_branch_base)]:
        result.fail(os.EX_USAGE,
                    _("Failed to create release branch based on {branch}.")
                    .format(branch=repr(command_context.selected_ref.name)),
                    _("Release branches (major.minor) can only be created off {branch}")
                    .format(branch=repr(context.config.release_branch_base))
                    )

    existing_release_branches = list(repotools.git_list_refs(context.repo, repotools.ref_name([
        const.REMOTES_PREFIX,
        context.config.remote_name,
        'release'])))

    release_branch_merge_bases = dict()
    for release_branch in context.get_release_branches():
        merge_base = repotools.git_merge_base(context.repo, context.config.release_branch_base, release_branch)
        if merge_base is None:
            result.fail(os.EX_DATAERR,
                        "Failed to resolve merge base.",
                        None)
        branch_refs = release_branch_merge_bases.get(merge_base)
        if branch_refs is None:
            release_branch_merge_bases[merge_base] = branch_refs = list()
        branch_refs.append(release_branch)

    latest_branch = None
    branch_points_on_same_commit = list()
    subsequent_branches = list()

    for history_commit in repotools.git_list_commits(
            context=context.repo,
            start=None,
            end=command_context.selected_commit,
            options=const.BRANCH_COMMIT_SCAN_OPTIONS):
        branch_refs = release_branch_merge_bases.get(history_commit.obj_name)
        if branch_refs is not None and len(branch_refs):
            branch_refs = list(
                filter(lambda tag_ref: context.release_branch_matcher.format(tag_ref.name) is not None,
                       branch_refs))
            if not len(branch_refs):
                continue

            branch_refs.sort(
                reverse=True,
                key=utils.cmp_to_key(
                    lambda tag_ref_a, tag_ref_b: semver.compare(
                        context.release_branch_matcher.format(tag_ref_a.name),
                        context.release_branch_matcher.format(tag_ref_b.name)
                    )
                )
            )
            if latest_branch is None:
                latest_branch = branch_refs[0]
            if history_commit.obj_name == command_context.selected_commit:
                branch_points_on_same_commit.extend(branch_refs)
            # for tag_ref in tag_refs:
            #     print('<<' + tag_ref.name)
            break

    for history_commit in repotools.git_list_commits(context.repo,
                                                     command_context.selected_commit,
                                                     command_context.selected_ref):
        branch_refs = release_branch_merge_bases.get(history_commit.obj_name)
        if branch_refs is not None and len(branch_refs):
            branch_refs = list(
                filter(lambda tag_ref: context.release_branch_matcher.format(tag_ref.name) is not None,
                       branch_refs))
            if not len(branch_refs):
                continue

            branch_refs.sort(
                reverse=True,
                key=utils.cmp_to_key(
                    lambda tag_ref_a, tag_ref_b: semver.compare(
                        context.release_branch_matcher.format(tag_ref_a.name),
                        context.release_branch_matcher.format(tag_ref_b.name)
                    )
                )
            )
            # for tag_ref in tag_refs:
            #     print('>>' + tag_ref.name)
            subsequent_branches.extend(branch_refs)

    if context.verbose:
        cli.print("Branches on same commit:\n"
                  + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in branch_points_on_same_commit))

        cli.print("Subsequent branches:\n"
                  + '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in subsequent_branches))

    if latest_branch is not None:
        latest_branch_version = context.release_branch_matcher.format(latest_branch.name)
        latest_branch_version_info = semver.parse_version_info(latest_branch_version)
    else:
        latest_branch_version = None
        latest_branch_version_info = None

    if latest_branch_version is not None:
        version_result = operation(context.config.version_config, latest_branch_version,
                                   get_global_sequence_number(context))
        result.add_subresult(version_result)

        new_version = version_result.value
        new_version_info = semver.parse_version_info(new_version)
    else:
        new_version_info = semver.parse_version_info(context.config.version_config.initial_version)
        new_version = version.format_version_info(new_version_info)

    scheme_procedures.get_sequence_number(context.config.version_config, new_version_info)

    if context.config.sequential_versioning:
        new_sequential_version = create_sequence_number_for_version(context, new_version)
    else:
        new_sequential_version = None

    try:
        config_in_selected_commit = read_config_in_commit(context.repo, command_context.selected_commit)
    except FileNotFoundError:
        config_in_selected_commit = dict()

    try:
        properties_in_selected_commit = read_properties_in_commit(context,
                                                                  context.repo,
                                                                  config_in_selected_commit,
                                                                  command_context.selected_commit)
    except FileNotFoundError:
        properties_in_selected_commit = dict()

    if not context.config.allow_shared_release_branch_base and len(branch_points_on_same_commit):
        result.fail(os.EX_USAGE,
                    _("Branch creation failed."),
                    _("Release branches cannot share a common ancestor commit.\n"
                      "Existing branches on commit {commit}:\n"
                      "{listing}")
                    .format(commit=command_context.selected_commit,
                            listing='\n'.join(' - ' + repr(tag_ref.name) for tag_ref in branch_points_on_same_commit)))

    if len(subsequent_branches):
        result.fail(os.EX_USAGE,
                    _("Branch creation failed."),
                    _("Subsequent release branches in history: %s\n")
                    % '\n'.join(' - ' + repr(tag_ref.name) for tag_ref in subsequent_branches))

    if context.config.tie_sequential_version_to_semantic_version \
            and len(existing_release_branches):
        prompt_result = prompt_for_confirmation(
            context=context,
            fail_title=_("Failed to create release branch based on {branch}.")
                .format(branch=repr(command_context.selected_ref.name)),
            message=_("This operation disables version increments "
                      "on all existing release branches.\n"
                      "Affected branches are:\n"
                      "{listing}")
                .format(listing=os.linesep.join(repr(branch.name) for branch in existing_release_branches))
            if not context.config.commit_version_property
            else _("This operation disables version increments on all existing branches.\n"
                   "Affected branches are:\n"
                   "{listing}")
                .format(listing=os.linesep.join(repr(branch.name) for branch in existing_release_branches)),
            prompt=_("Continue?"),
        )
        result.add_subresult(prompt_result)
        if result.has_errors() or not prompt_result.value:
            return result

    if not result.has_errors():
        if new_version is None:
            result.error(os.EX_SOFTWARE,
                         _("Internal error."),
                         _("Missing result version.")
                         )
        if latest_branch_version is not None and semver.compare(latest_branch_version, new_version) >= 0:
            result.error(os.EX_DATAERR,
                         _("Failed to increment version from {current_version} to {new_version}.")
                         .format(current_version=repr(latest_branch_version), new_version=repr(new_version)),
                         _("The new version is lower than or equal to the current version.")
                         )
        result.abort_on_error()

        branch_name = get_branch_name_for_version(context, new_version_info)
        tag_name = get_tag_name_for_version(context, new_version_info)

        clone_result = clone_repository(context, context.config.release_branch_base)
        cloned_repo: RepoContext = clone_result.value

        # run version change hooks on new release branch
        git_or_fail(cloned_repo, result, ['checkout', '--force',
                                          '-b', branch_name,
                                          command_context.selected_commit],
                    _("Failed to check out release branch."))

        clone_context: Context = create_temp_context(context, result, cloned_repo.dir)
        clone_context.config.remote_name = 'origin'

        commit_info = CommitInfo()
        commit_info.add_message("#version: " + cli.if_none(new_version))

        if (context.config.commit_version_property and new_version is not None) \
                or (context.config.commit_sequential_version_property and new_sequential_version is not None):

            update_result = update_project_property_file(clone_context,
                                                         properties_in_selected_commit,
                                                         new_version,
                                                         new_sequential_version,
                                                         commit_info)
            result.add_subresult(update_result)
            if result.has_errors():
                result.fail(os.EX_DATAERR,
                            _("Property update failed."),
                            _("An unexpected error occurred.")
                            )

        if new_version is not None:
            execute_version_change_actions(clone_context, latest_branch_version, new_version)

        if commit_info is not None:
            if command_context.selected_commit != command_context.selected_ref.target.obj_name:
                result.fail(os.EX_USAGE,
                            _("Failed to commit version update."),
                            _("The selected parent commit {commit} does not represent the tip of {branch}.")
                            .format(commit=command_context.selected_commit,
                                    branch=repr(command_context.selected_ref.name))
                            )

            # commit changes
            commit_info.add_parent(command_context.selected_commit)
            object_to_tag = create_commit(clone_context, result, commit_info)
        else:
            object_to_tag = command_context.selected_commit

        # show info and prompt for confirmation
        cli.print("ref                 : " + cli.if_none(command_context.selected_ref.name))
        cli.print("ref_" + const.DEFAULT_VERSION_VAR_NAME + "         : " + cli.if_none(latest_branch_version))
        cli.print("new_branch          : " + cli.if_none(branch_name))
        cli.print("new_" + const.DEFAULT_VERSION_VAR_NAME + "         : " + cli.if_none(new_version))
        cli.print("selected object     : " + cli.if_none(command_context.selected_commit))
        cli.print("tagged object       : " + cli.if_none(object_to_tag))

        prompt_result = prompt_for_confirmation(
            context=context,
            fail_title=_("Failed to create release branch based on {branch}.")
                .format(branch=repr(command_context.selected_ref.name)),
            message=_("The branch and tags are about to be pushed."),
            prompt=_("Continue?"),
        )
        result.add_subresult(prompt_result)
        if result.has_errors() or not prompt_result.value:
            return result

        # push atomically
        push_command = ['push', '--atomic']
        if context.dry_run:
            push_command.append('--dry-run')
        if context.verbose:
            push_command.append('--verbose')
        push_command.append(context.config.remote_name)

        # push the base branch commit
        # push_command.append(commit + ':' + const.LOCAL_BRANCH_PREFIX + selected_ref.local_branch_name)

        # push the new branch or fail if it exists
        push_command.extend(
            ['--force-with-lease=' + repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, branch_name) + ':',
             repotools.ref_target(object_to_tag) + ':' + repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX,
                                                                                   branch_name)])
        # push the new version tag or fail if it exists
        push_command.extend(['--force-with-lease=' + repotools.create_ref_name(const.LOCAL_TAG_PREFIX, tag_name) + ':',
                             repotools.ref_target(object_to_tag) + ':' + repotools.create_ref_name(
                                 const.LOCAL_TAG_PREFIX, tag_name)])

        git_or_fail(cloned_repo, result, push_command, _("Failed to push."))

    return result