.. versionadded:: 2015.8.0

    Interface to `git-merge-base(1)`_.

    cwd
        The path to the git checkout

    refs
        Any refs/commits to check for a merge base. Can be passed as a
        comma-separated list or a Python list.

    all : False
        Return a list of all matching merge bases. Not compatible with any of
        the below options except for ``octopus``.

    octopus : False
        If ``True``, then this function will determine the best common
        ancestors of all specified commits, in preparation for an n-way merge.
        See here_ for a description of how these bases are determined.

        Set ``all`` to ``True`` with this option to return all computed merge
        bases, otherwise only the "best" will be returned.

    is_ancestor : False
        If ``True``, then instead of returning the merge base, return a
        boolean telling whether or not the first commit is an ancestor of the
        second commit.

        .. note::
            This option requires two commits to be passed.

        .. versionchanged:: 2015.8.2
            Works properly in git versions older than 1.8.0, where the
            ``--is-ancestor`` CLI option is not present.

    independent : False
        If ``True``, this function will return the IDs of the refs/commits
        passed which cannot be reached by another commit.

    fork_point
        If passed, then this function will return the commit where the
        commit diverged from the ref specified by ``fork_point``. If no fork
        point is found, ``None`` is returned.

        .. note::
            At most one commit is permitted to be passed if a ``fork_point`` is
            specified. If no commits are passed, then ``HEAD`` is assumed.

    opts
        Any additional options to add to the command line, in a single string

        .. note::
            On the Salt CLI, if the opts are preceded with a dash, it is
            necessary to precede them with ``opts=`` (as in the CLI examples
            below) to avoid causing errors with Salt's own argument parsing.

            This option should not be necessary unless new CLI arguments are
            added to `git-merge-base(1)`_ and are not yet supported in Salt.

    git_opts
        Any additional options to add to git command itself (not the
        ``merge-base`` subcommand), in a single string. This is useful for
        passing ``-c`` to run git with temporary changes to the git
        configuration.

        .. versionadded:: 2017.7.0

        .. note::
            This is only supported in git 1.7.2 and newer.

    user
        User under which to run the git command. By default, the command is run
        by the user under which the minion is running.

    password
        Windows only. Required when specifying ``user``. This parameter will be
        ignored on non-Windows platforms.

      .. versionadded:: 2016.3.4

    ignore_retcode : False
        if ``True``, do not log an error to the minion log if the git command
        returns a nonzero exit status.

    output_encoding
        Use this option to specify which encoding to use to decode the output
        from any git commands which are run. This should not be needed in most
        cases.

        .. note::
            This should only be needed if the files in the repository were
            created with filenames using an encoding other than UTF-8 to handle
            Unicode characters.

        .. versionadded:: 2018.3.1

    .. _`git-merge-base(1)`: http://git-scm.com/docs/git-merge-base
    .. _here: http://git-scm.com/docs/git-merge-base#_discussion

    CLI Examples:

    .. code-block:: bash

        salt myminion git.merge_base /path/to/repo HEAD upstream/mybranch
        salt myminion git.merge_base /path/to/repo 8f2e542,4ad8cab,cdc9886 octopus=True
        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab,cdc9886 independent=True
        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab is_ancestor=True
        salt myminion git.merge_base /path/to/repo fork_point=upstream/master
        salt myminion git.merge_base /path/to/repo refs=mybranch fork_point=upstream/master

def merge_base(cwd,
               refs=None,
               octopus=False,
               is_ancestor=False,
               independent=False,
               fork_point=None,
               opts='',
               git_opts='',
               user=None,
               password=None,
               ignore_retcode=False,
               output_encoding=None,
               **kwargs):
    '''
    .. versionadded:: 2015.8.0

    Interface to `git-merge-base(1)`_.

    cwd
        The path to the git checkout

    refs
        Any refs/commits to check for a merge base. Can be passed as a
        comma-separated list or a Python list.

    all : False
        Return a list of all matching merge bases. Not compatible with any of
        the below options except for ``octopus``.

    octopus : False
        If ``True``, then this function will determine the best common
        ancestors of all specified commits, in preparation for an n-way merge.
        See here_ for a description of how these bases are determined.

        Set ``all`` to ``True`` with this option to return all computed merge
        bases, otherwise only the "best" will be returned.

    is_ancestor : False
        If ``True``, then instead of returning the merge base, return a
        boolean telling whether or not the first commit is an ancestor of the
        second commit.

        .. note::
            This option requires two commits to be passed.

        .. versionchanged:: 2015.8.2
            Works properly in git versions older than 1.8.0, where the
            ``--is-ancestor`` CLI option is not present.

    independent : False
        If ``True``, this function will return the IDs of the refs/commits
        passed which cannot be reached by another commit.

    fork_point
        If passed, then this function will return the commit where the
        commit diverged from the ref specified by ``fork_point``. If no fork
        point is found, ``None`` is returned.

        .. note::
            At most one commit is permitted to be passed if a ``fork_point`` is
            specified. If no commits are passed, then ``HEAD`` is assumed.

    opts
        Any additional options to add to the command line, in a single string

        .. note::
            On the Salt CLI, if the opts are preceded with a dash, it is
            necessary to precede them with ``opts=`` (as in the CLI examples
            below) to avoid causing errors with Salt's own argument parsing.

            This option should not be necessary unless new CLI arguments are
            added to `git-merge-base(1)`_ and are not yet supported in Salt.

    git_opts
        Any additional options to add to git command itself (not the
        ``merge-base`` subcommand), in a single string. This is useful for
        passing ``-c`` to run git with temporary changes to the git
        configuration.

        .. versionadded:: 2017.7.0

        .. note::
            This is only supported in git 1.7.2 and newer.

    user
        User under which to run the git command. By default, the command is run
        by the user under which the minion is running.

    password
        Windows only. Required when specifying ``user``. This parameter will be
        ignored on non-Windows platforms.

      .. versionadded:: 2016.3.4

    ignore_retcode : False
        if ``True``, do not log an error to the minion log if the git command
        returns a nonzero exit status.

    output_encoding
        Use this option to specify which encoding to use to decode the output
        from any git commands which are run. This should not be needed in most
        cases.

        .. note::
            This should only be needed if the files in the repository were
            created with filenames using an encoding other than UTF-8 to handle
            Unicode characters.

        .. versionadded:: 2018.3.1

    .. _`git-merge-base(1)`: http://git-scm.com/docs/git-merge-base
    .. _here: http://git-scm.com/docs/git-merge-base#_discussion

    CLI Examples:

    .. code-block:: bash

        salt myminion git.merge_base /path/to/repo HEAD upstream/mybranch
        salt myminion git.merge_base /path/to/repo 8f2e542,4ad8cab,cdc9886 octopus=True
        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab,cdc9886 independent=True
        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab is_ancestor=True
        salt myminion git.merge_base /path/to/repo fork_point=upstream/master
        salt myminion git.merge_base /path/to/repo refs=mybranch fork_point=upstream/master
    '''
    cwd = _expand_path(cwd, user)
    kwargs = salt.utils.args.clean_kwargs(**kwargs)
    all_ = kwargs.pop('all', False)
    if kwargs:
        salt.utils.args.invalid_kwargs(kwargs)

    if all_ and (independent or is_ancestor or fork_point):
        raise SaltInvocationError(
            'The \'all\' argument is not compatible with \'independent\', '
            '\'is_ancestor\', or \'fork_point\''
        )

    if refs is None:
        refs = []
    elif not isinstance(refs, (list, tuple)):
        refs = [x.strip() for x in six.text_type(refs).split(',')]
    mutually_exclusive_count = len(
        [x for x in (octopus, independent, is_ancestor, fork_point) if x]
    )
    if mutually_exclusive_count > 1:
        raise SaltInvocationError(
            'Only one of \'octopus\', \'independent\', \'is_ancestor\', and '
            '\'fork_point\' is permitted'
        )
    elif is_ancestor:
        if len(refs) != 2:
            raise SaltInvocationError(
                'Two refs/commits are required if \'is_ancestor\' is True'
            )
    elif fork_point:
        if len(refs) > 1:
            raise SaltInvocationError(
                'At most one ref/commit can be passed if \'fork_point\' is '
                'specified'
            )
        elif not refs:
            refs = ['HEAD']

    if is_ancestor:
        if _LooseVersion(version(versioninfo=False)) < _LooseVersion('1.8.0'):
            # Pre 1.8.0 git doesn't have --is-ancestor, so the logic here is a
            # little different. First we need to resolve the first ref to a
            # full SHA1, and then if running git merge-base on both commits
            # returns an identical commit to the resolved first ref, we know
            # that the first ref is an ancestor of the second ref.
            first_commit = rev_parse(cwd,
                                     rev=refs[0],
                                     opts=['--verify'],
                                     user=user,
                                     password=password,
                                     ignore_retcode=ignore_retcode,
                                     output_encoding=output_encoding)
            return merge_base(cwd,
                              refs=refs,
                              is_ancestor=False,
                              user=user,
                              password=password,
                              ignore_retcode=ignore_retcode,
                              output_encoding=output_encoding) == first_commit

    command = ['git'] + _format_git_opts(git_opts)
    command.append('merge-base')
    command.extend(_format_opts(opts))
    if all_:
        command.append('--all')
    if octopus:
        command.append('--octopus')
    elif is_ancestor:
        command.append('--is-ancestor')
    elif independent:
        command.append('--independent')
    elif fork_point:
        command.extend(['--fork-point', fork_point])
    command.extend(refs)
    result = _git_run(command,
                      cwd=cwd,
                      user=user,
                      password=password,
                      ignore_retcode=ignore_retcode,
                      failhard=False if is_ancestor else True,
                      output_encoding=output_encoding)
    if is_ancestor:
        return result['retcode'] == 0
    all_bases = result['stdout'].splitlines()
    if all_:
        return all_bases
    return all_bases[0]