示例#1
0
def test_latest_without_target():
    """
    Test latest when called without passing target
    """
    name = "https://foo.com/bar/baz.git"
    with pytest.raises(TypeError):
        # pylint: disable=E1120
        git_state.latest(name)
示例#2
0
    def test_latest_no_diff_for_bare_repo(self, target):
        '''
        This test ensures that we don't attempt to diff when cloning a repo
        using either bare=True or mirror=True.
        '''
        name = 'https://foo.com/bar/baz.git'
        gitdir = os.path.join(target, 'refs')
        isdir_mock = MagicMock(
            side_effect=lambda path: DEFAULT if path != gitdir else True)

        branches = ['foo', 'bar', 'baz']
        tags = ['v1.1.0', 'v.1.1.1', 'v1.2.0']
        local_head = 'b9ef06ab6b7524eb7c27d740dbbd5109c6d75ee4'
        remote_head = 'eef672c1ec9b8e613905dbcd22a4612e31162807'

        git_diff = Mock()
        dunder_salt = {
            'git.current_branch':
            MagicMock(return_value=branches[0]),
            'git.config_get_regexp':
            MagicMock(return_value={}),
            'git.diff':
            git_diff,
            'git.fetch':
            MagicMock(return_value={}),
            'git.is_worktree':
            MagicMock(return_value=False),
            'git.list_branches':
            MagicMock(return_value=branches),
            'git.list_tags':
            MagicMock(return_value=tags),
            'git.remote_refs':
            MagicMock(return_value={'HEAD': remote_head}),
            'git.remotes':
            MagicMock(return_value={
                'origin': {
                    'fetch': name,
                    'push': name
                },
            }),
            'git.rev_parse':
            MagicMock(side_effect=git_state.CommandExecutionError()),
            'git.revision':
            MagicMock(return_value=local_head),
            'git.version':
            MagicMock(return_value='1.8.3.1'),
        }
        with patch('os.path.isdir', isdir_mock), \
                patch.dict(git_state.__salt__, dunder_salt):
            result = git_state.latest(
                name=name,
                target=target,
                mirror=True,  # mirror=True implies bare=True
            )
            assert result['result'] is True, result
            git_diff.assert_not_called()
示例#3
0
    def test_latest_no_diff_for_bare_repo(self, target):
        """
        This test ensures that we don't attempt to diff when cloning a repo
        using either bare=True or mirror=True.
        """
        name = "https://foo.com/bar/baz.git"
        gitdir = os.path.join(target, "refs")
        isdir_mock = MagicMock(
            side_effect=lambda path: DEFAULT if path != gitdir else True)

        branches = ["foo", "bar", "baz"]
        tags = ["v1.1.0", "v.1.1.1", "v1.2.0"]
        local_head = "b9ef06ab6b7524eb7c27d740dbbd5109c6d75ee4"
        remote_head = "eef672c1ec9b8e613905dbcd22a4612e31162807"

        git_diff = Mock()
        dunder_salt = {
            "git.current_branch":
            MagicMock(return_value=branches[0]),
            "git.config_get_regexp":
            MagicMock(return_value={}),
            "git.diff":
            git_diff,
            "git.fetch":
            MagicMock(return_value={}),
            "git.is_worktree":
            MagicMock(return_value=False),
            "git.list_branches":
            MagicMock(return_value=branches),
            "git.list_tags":
            MagicMock(return_value=tags),
            "git.remote_refs":
            MagicMock(return_value={"HEAD": remote_head}),
            "git.remotes":
            MagicMock(return_value={"origin": {
                "fetch": name,
                "push": name
            }}),
            "git.rev_parse":
            MagicMock(side_effect=git_state.CommandExecutionError()),
            "git.revision":
            MagicMock(return_value=local_head),
            "git.version":
            MagicMock(return_value="1.8.3.1"),
        }
        with patch("os.path.isdir",
                   isdir_mock), patch.dict(git_state.__salt__, dunder_salt):
            result = git_state.latest(
                name=name,
                target=target,
                mirror=True,  # mirror=True implies bare=True
            )
            assert result["result"] is True, result
            git_diff.assert_not_called()
示例#4
0
文件: git_test.py 项目: zeus911/ops
def sub_test_latest(self, arg):
    '''
        Sub part of test_latest
    '''
    mock = MagicMock(return_value=0)
    with patch.dict(git.__salt__, {'git.checkout': mock}):
        mock = MagicMock(return_value=0)
        with patch.dict(git.__salt__, {'git.config_get': mock}):
            with patch.dict(git.__opts__, {'test': True}):
                mock = MagicMock(return_value="salt")
                with patch.object(git, '_neutral_test', mock):
                    self.assertEqual(git.latest(arg[0], True, "salt"), "salt")
示例#5
0
文件: git_test.py 项目: DaveQB/salt
def sub_test_latest(self, arg):
    """
        Sub part of test_latest
    """
    mock = MagicMock(return_value=0)
    with patch.dict(git.__salt__, {"git.checkout": mock}):
        mock = MagicMock(return_value=0)
        with patch.dict(git.__salt__, {"git.config_get": mock}):
            with patch.dict(git.__opts__, {"test": True}):
                mock = MagicMock(return_value="salt")
                with patch.object(git, "_neutral_test", mock):
                    self.assertEqual(git.latest(arg[0], True, "salt"), "salt")
示例#6
0
def latest(*args, **kwargs):
    '''
    Compat wrapper
    '''
    # KISS: let do the rest of the job by original git.latest
    locs = globals()
    locs.update(locals())
    for i in [
            '__env__',
            '__grains__',
            '__lowstate__',
            '__opts__',
            '__package__',
            '__pillar__',
            '__running__',
            '__salt__',
    ]:
        if not hasattr(git, i):
            setattr(git, i, locs[i])
    return git.latest(*args, **kwargs)
示例#7
0
文件: git_test.py 项目: zeus911/ops
    def test_latest(self):
        '''
            Test to make sure the repository is cloned and is up to date
        '''
        arg = ["[email protected]:user/website.git"]
        ret = {
            'changes': {
                'new': '[email protected]:user/website.git',
                'revision': None
            },
            'comment': 'Repository [email protected].'
            'com:user/website.git cloned to salt',
            'name': '[email protected]:user/website.git',
            'result': True
        }

        mock = MagicMock(
            return_value={
                'result':
                False,
                'comment':
                '"rev"'
                'is not compatible with the "mirror"'
                'and "bare" arguments'
            })
        with patch.object(git, '_fail', mock):
            self.assertDictEqual(
                git.latest("[email protected]."
                           "com:user/website.git",
                           True,
                           mirror=True,
                           bare=True), {
                               'comment': '"rev"is not compatible with the'
                               ' "mirror"and "bare" arguments',
                               'result': False
                           })

        mock = MagicMock(return_value={
            'result': False,
            'comment': '"target" option'
            ' is required'
        })
        with patch.object(git, '_fail', mock):
            self.assertDictEqual(
                git.latest("[email protected]:"
                           "user/website.git"), {
                               'comment': '"target" option is required',
                               'result': False
                           })

        with patch.dict(git.__grains__, {"shell": True}):
            mock = MagicMock(
                return_value={
                    'comment': 'onlyif execution'
                    ' failed',
                    'skip_watch': True,
                    'result': True
                })
            with patch.object(git, 'mod_run_check', mock):
                self.assertDictEqual(
                    git.latest("[email protected]:"
                               "user/website.git",
                               target="/usr/share/nginx/prod",
                               onlyif=True), {
                                   'changes': {},
                                   'comment': 'onlyif execution failed',
                                   'name': '[email protected]:'
                                   'user/website.git',
                                   'result': True,
                                   'skip_watch': True
                               })

            mock = MagicMock(return_value="salt")
            with patch.object(git, 'mod_run_check', mock):
                mock = MagicMock(return_value=True)
                with patch.object(os.path, 'isdir', mock):
                    mock = MagicMock(return_value=Exception)
                    with patch.dict(git.__salt__, {'git.revision': mock}):
                        mock = MagicMock(return_value="salt")
                        with patch.object(git, '_fail', mock):
                            self.assertEqual(
                                git.latest(
                                    "git@gitl"
                                    "ab.example.com:user"
                                    "/website.git",
                                    target="/usr/share/n"
                                    "ginx/prod"), "salt")

                    mock = MagicMock(return_value="salt")
                    with patch.dict(git.__salt__, {'git.revision': mock}):
                        with patch.dict(git.__salt__,
                                        {'git.current_branch': mock}):
                            mock = MagicMock(return_value=None)
                            with patch.dict(git.__salt__,
                                            {'git.ls_remote': mock}):
                                with patch.dict(git.__opts__, {'test': True}):
                                    mock = MagicMock(return_value=["salt"])
                                    with patch.object(git, '_neutral_test',
                                                      mock):
                                        self.assertListEqual(
                                            git.latest(arg[0], None, "salt"),
                                            ["salt"])

                                with patch.dict(git.__opts__, {'test': False}):
                                    mock = MagicMock(return_value=[arg[0]])
                                    with patch.dict(git.__salt__,
                                                    {'git.remote_get': mock}):
                                        mock = MagicMock(return_value=0)
                                        with patch.dict(
                                                git.__salt__,
                                            {'cmd.retcode': mock}):
                                            sub_test_latest(self, arg)

                mock = MagicMock(return_value=False)
                with patch.object(os.path, 'isdir', mock):
                    mock = MagicMock(side_effect=[False, True])
                    with patch.object(os.path, 'isdir', mock):
                        mock = MagicMock(return_value=True)
                        with patch.object(os, 'listdir', mock):
                            mock = MagicMock(return_value=["salt"])
                            with patch.object(git, '_fail', mock):
                                self.assertListEqual(
                                    git.latest(arg[0], None, "salt"), ["salt"])

                    with patch.dict(git.__opts__, {'test': True}):
                        mock = MagicMock(return_value=["salt"])
                        with patch.object(git, '_neutral_test', mock):
                            self.assertListEqual(
                                git.latest(arg[0], None, "salt"), ["salt"])

                    with patch.dict(git.__opts__, {'test': False}):
                        mock = MagicMock(side_effect=[Exception, True])
                        with patch.dict(git.__salt__, {'git.clone': mock}):
                            mock = MagicMock(return_value=["salt"])
                            with patch.object(git, '_fail', mock):
                                self.assertListEqual(
                                    git.latest(arg[0], None, "salt"), ["salt"])

                            self.assertEqual(
                                git.latest(arg[0], None, "salt", bare=True),
                                ret)
示例#8
0
文件: git_test.py 项目: DaveQB/salt
    def test_latest(self):
        """
            Test to make sure the repository is cloned and is up to date
        """
        arg = ["[email protected]:user/website.git"]
        ret = {
            "changes": {"new": "[email protected]:user/website.git", "revision": None},
            "comment": "Repository [email protected]." "com:user/website.git cloned to salt",
            "name": "[email protected]:user/website.git",
            "result": True,
        }

        mock = MagicMock(
            return_value={
                "result": False,
                "comment": '"rev"' 'is not compatible with the "mirror"' 'and "bare" arguments',
            }
        )
        with patch.object(git, "_fail", mock):
            self.assertDictEqual(
                git.latest("[email protected]." "com:user/website.git", True, mirror=True, bare=True),
                {"comment": '"rev"is not compatible with the' ' "mirror"and "bare" arguments', "result": False},
            )

        mock = MagicMock(return_value={"result": False, "comment": '"target" option' " is required"})
        with patch.object(git, "_fail", mock):
            self.assertDictEqual(
                git.latest("[email protected]:" "user/website.git"),
                {"comment": '"target" option is required', "result": False},
            )

        with patch.dict(git.__grains__, {"shell": True}):
            mock = MagicMock(return_value={"comment": "onlyif execution" " failed", "skip_watch": True, "result": True})
            with patch.object(git, "mod_run_check", mock):
                self.assertDictEqual(
                    git.latest(
                        "[email protected]:" "user/website.git", target="/usr/share/nginx/prod", onlyif=True
                    ),
                    {
                        "changes": {},
                        "comment": "onlyif execution failed",
                        "name": "[email protected]:" "user/website.git",
                        "result": True,
                        "skip_watch": True,
                    },
                )

            mock = MagicMock(return_value="salt")
            with patch.object(git, "mod_run_check", mock):
                mock = MagicMock(return_value=True)
                with patch.object(os.path, "isdir", mock):
                    mock = MagicMock(return_value=Exception)
                    with patch.dict(git.__salt__, {"git.revision": mock}):
                        mock = MagicMock(return_value="salt")
                        with patch.object(git, "_fail", mock):
                            self.assertEqual(
                                git.latest(
                                    "git@gitl" "ab.example.com:user" "/website.git", target="/usr/share/n" "ginx/prod"
                                ),
                                "salt",
                            )

                    mock = MagicMock(return_value="salt")
                    with patch.dict(git.__salt__, {"git.revision": mock}):
                        with patch.dict(git.__salt__, {"git.current_branch": mock}):
                            mock = MagicMock(return_value=None)
                            with patch.dict(git.__salt__, {"git.ls_remote": mock}):
                                with patch.dict(git.__opts__, {"test": True}):
                                    mock = MagicMock(return_value=["salt"])
                                    with patch.object(git, "_neutral_test", mock):
                                        self.assertListEqual(git.latest(arg[0], None, "salt"), ["salt"])

                                with patch.dict(git.__opts__, {"test": False}):
                                    mock = MagicMock(return_value=[arg[0]])
                                    with patch.dict(git.__salt__, {"git.remote_get": mock}):
                                        mock = MagicMock(return_value=0)
                                        with patch.dict(git.__salt__, {"cmd.retcode": mock}):
                                            sub_test_latest(self, arg)

                mock = MagicMock(return_value=False)
                with patch.object(os.path, "isdir", mock):
                    mock = MagicMock(side_effect=[False, True])
                    with patch.object(os.path, "isdir", mock):
                        mock = MagicMock(return_value=True)
                        with patch.object(os, "listdir", mock):
                            mock = MagicMock(return_value=["salt"])
                            with patch.object(git, "_fail", mock):
                                self.assertListEqual(git.latest(arg[0], None, "salt"), ["salt"])

                    with patch.dict(git.__opts__, {"test": True}):
                        mock = MagicMock(return_value=["salt"])
                        with patch.object(git, "_neutral_test", mock):
                            self.assertListEqual(git.latest(arg[0], None, "salt"), ["salt"])

                    with patch.dict(git.__opts__, {"test": False}):
                        mock = MagicMock(side_effect=[Exception, True])
                        with patch.dict(git.__salt__, {"git.clone": mock}):
                            mock = MagicMock(return_value=["salt"])
                            with patch.object(git, "_fail", mock):
                                self.assertListEqual(git.latest(arg[0], None, "salt"), ["salt"])

                            self.assertEqual(git.latest(arg[0], None, "salt", bare=True), ret)
示例#9
0
def latest(name,
           rev=None,
           target=None,
           runas=None,
           user=None,
           force=None,
           force_checkout=False,
           submodules=False,
           mirror=False,
           bare=False,
           remote_name='origin',
           always_fetch=False,
           identity=None,
           onlyif=False,
           unless=False,
           firstrun=True):
    '''
    Make sure the repository is cloned to the given directory and is up to date
    Thin wrapper to git.latest that also makes
    a git merge --only-ff to merge unharmful commits
    without failling hard

    name
        Address of the remote repository as passed to "git clone"

    rev
        The remote branch, tag, or revision ID to checkout after
        clone / before update

    target
        Name of the target directory where repository is about to be cloned

    runas
        Name of the user performing repository management operations

        .. deprecated:: 0.17.0

    user
        Name of the user performing repository management operations

        .. versionadded:: 0.17.0

    force
        Force git to clone into pre-existing directories (deletes contents)

    force_checkout
        Force a checkout even if there might be overwritten changes
        (Default: False)

    submodules
        Update submodules on clone or branch change (Default: False)

    mirror
        True if the repository is to be a mirror of the remote repository.
        This implies bare, and thus is incompatible with rev.

    bare
        True if the repository is to be a bare clone of the remote repository.
        This is incompatible with rev, as nothing will be checked out.

    remote_name
        defines a different remote name.
        For the first clone the given name is set to the default remote,
        else it is just a additional remote. (Default: 'origin')

    always_fetch
        If a tag or branch name is used as the rev a fetch will not occur
        until the tag or branch name changes. Setting this to true will force
        a fetch to occur. Only applies when rev is set. (Default: False)

    identity
        A path to a private key to use over SSH

    onlyif
        A command to run as a check, run the named command only if the command
        passed to the ``onlyif`` option returns true

    unless
        A command to run as a check, only run the named command if the command
        passed to the ``unless`` option returns false
    '''
    ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}

    # Check to make sure rev and mirror/bare are not both in use
    if rev and (mirror or bare):
        return _fail(ret, ('"rev" is not compatible with the "mirror" and '
                           '"bare" arguments'))

    if not target:
        return _fail(ret, '"target" option is required')
    if user is not None and runas is not None:
        # user wins over runas but let warn about the deprecation.
        ret.setdefault('warnings', []).append(
            'Passed both the \'runas\' and \'user\' arguments. Please don\'t. '
            '\'runas\' is being ignored in favor of \'user\'.'
        )
        runas = None
    elif runas is not None:
        # Support old runas usage
        user = runas
        runas = None

    run_check_cmd_kwargs = {'runas': user}

    # check if git.latest should be applied
    cret = _run_check(
        run_check_cmd_kwargs, onlyif, unless
    )
    if isinstance(cret, dict):
        ret.update(cret)
        return ret

    bare = bare or mirror
    check = 'refs' if bare else '.git'

    if os.path.isdir(target) and os.path.isdir('{0}/{1}'.format(target,
                                                                check)):
        # git pull is probably required
        log.debug(('target {0} is found, "git pull" '
                   'is probably required'.format(target)))
        try:
            current_rev = __salt__['git.revision'](target, user=user)

            # handle the case where a branch was provided for rev
            remote_rev = None
            branch = __salt__['git.current_branch'](target, user=user)
            # We're only interested in the remote branch if a branch
            # (instead of a hash, for example) was provided for rev.
            if len(branch) > 0 and branch == rev:
                remote_rev = __salt__['git.ls_remote'](target,
                                                       repository=name,
                                                       branch=branch, user=user,
                                                       identity=identity)

            # only do something, if the specified rev differs from the
            # current_rev and remote_rev
            if current_rev in [rev, remote_rev]:
                new_rev = current_rev
            else:

                if __opts__['test']:
                    return _neutral_test(
                        ret,
                        ('Repository {0} update is probably required (current '
                         'revision is {1})').format(target, current_rev))

                # if remote_name is defined set fetch_opts to remote_name
                if remote_name != 'origin':
                    fetch_opts = remote_name
                else:
                    fetch_opts = ''

                # check remote if fetch_url not == name set it
                remote = __salt__['git.remote_get'](target,
                                                    remote=remote_name,
                                                    user=user)
                if remote is None or remote[0] != name:
                    __salt__['git.remote_set'](target,
                                               name=remote_name,
                                               url=name,
                                               user=user)
                    ret['changes']['remote/{0}'.format(remote_name)] = "{0} => {1}".format(str(remote), name)

                # caheck if rev is already present in repo, git-fetch otherwise
                if bare:
                    __salt__['git.fetch'](target,
                                          opts=fetch_opts,
                                          user=user,
                                          identity=identity)
                elif rev:

                    cmd = "git rev-parse " + rev + '^{commit}'
                    retcode = __salt__['cmd.retcode'](cmd,
                                                      cwd=target,
                                                      runas=user)
                    # there is a issues #3938 addressing this
                    if 0 != retcode or always_fetch:
                        __salt__['git.fetch'](target,
                                              opts=fetch_opts,
                                              user=user,
                                              identity=identity)

                    __salt__['git.checkout'](target,
                                             rev,
                                             force=force_checkout,
                                             user=user)

                # check if we are on a branch to merge changes
                cmd = "git symbolic-ref -q HEAD > /dev/null"
                retcode = __salt__['cmd.retcode'](cmd, cwd=target, runas=user)
                # XXX: the real different with git.latest is here
                # we pull
                # but also fetch and merge --only-ff in case of errors
                if 0 == retcode:
                    method = 'git.fetch' if bare else 'git.pull'
                    def pull():
                        __salt__[method](
                            target,
                            opts=fetch_opts,
                            user=user,
                            identity=identity)
                    try:
                        pull()
                    except Exception, ex:
                        ex_msg = ex.message.lower()
                        # if the pb is the remote branch not being set
                        # just set it and run pull again
                        if(
                            ("--set-upstream" in ex_msg)
                            and firstrun
                            and len(branch) > 0
                            and branch == rev
                        ):
                            cmd = 'git branch --set-upstream {branch} origin/{rev}'.format(
                                branch=branch,
                                rev=rev,
                            )
                            __salt__['cmd.run_stdout'](cmd, cwd=target, runas=user)
                            pull()
                        else:
                            if (
                                (method == 'git.fetch')
                                or
                                (not 'local changes' in ex_msg)
                            ):
                                raise
                            __salt__['git.fetch'](
                                target,
                                opts=fetch_opts,
                                user=user,
                                identity=identity)
                            __salt__['git.fetch'](
                                target,
                                opts='--tags',
                                user=user,
                                identity=identity)
                            __salt__['git.merge'](
                                target,
                                opts='--ff-only',
                                user=user)
                if submodules:
                    __salt__['git.submodule'](target,
                                              user=user,
                                              identity=identity,
                                              opts='--recursive')

                new_rev = __salt__['git.revision'](cwd=target, user=user)
        except Exception as exc:
            return _fail( ret, str(exc))

        if current_rev != new_rev:
            log.info('Repository {0} updated: {1} => {2}'.format(target,
                                                                 current_rev,
                                                                 new_rev))
            ret['comment'] = 'Repository {0} updated'.format(target)
            ret['changes']['revision'] = '{0} => {1}'.format(
                current_rev, new_rev)
    else:
        # KISS: let do the rest of the job by original git.latest
        locs = globals()
        locs.update(locals())
        for i in [
            '__env__',
            '__grains__',
            '__lowstate__',
            '__opts__',
            '__package__',
            '__pillar__',
            '__running__',
            '__salt__',
        ]:
            if not hasattr(git, i):
                setattr(git, i, locs[i])
        return git.latest(
            name,
            rev=rev,
            target=target,
            runas=runas,
            user=user,
            force=force,
            force_checkout=force_checkout,
            submodules=submodules,
            mirror=mirror,
            bare=bare,
            remote_name=remote_name,
            always_fetch=always_fetch,
            identity=identity,
            onlyif=onlyif,
            unless=unless)
    return ret
示例#10
0
def old_latest(name,
               rev=None,
               target=None,
               runas=None,
               user=None,
               force=None,
               force_checkout=False,
               submodules=False,
               mirror=False,
               bare=False,
               remote_name='origin',
               always_fetch=False,
               identity=None,
               onlyif=False,
               unless=False,
               firstrun=True):
    '''
    Make sure the repository is cloned to the given directory and is up to date
    Thin wrapper to git.latest that also makes
    a git merge --only-ff to merge unharmful commits
    without failling hard

    name
        Address of the remote repository as passed to "git clone"

    rev
        The remote branch, tag, or revision ID to checkout after
        clone / before update

    target
        Name of the target directory where repository is about to be cloned

    runas
        Name of the user performing repository management operations

        .. deprecated:: 0.17.0

    user
        Name of the user performing repository management operations

        .. versionadded:: 0.17.0

    force
        Force git to clone into pre-existing directories (deletes contents)

    force_checkout
        Force a checkout even if there might be overwritten changes
        (Default: False)

    submodules
        Update submodules on clone or branch change (Default: False)

    mirror
        True if the repository is to be a mirror of the remote repository.
        This implies bare, and thus is incompatible with rev.

    bare
        True if the repository is to be a bare clone of the remote repository.
        This is incompatible with rev, as nothing will be checked out.

    remote_name
        defines a different remote name.
        For the first clone the given name is set to the default remote,
        else it is just a additional remote. (Default: 'origin')

    always_fetch
        If a tag or branch name is used as the rev a fetch will not occur
        until the tag or branch name changes. Setting this to true will force
        a fetch to occur. Only applies when rev is set. (Default: False)

    identity
        A path to a private key to use over SSH

    onlyif
        A command to run as a check, run the named command only if the command
        passed to the ``onlyif`` option returns true

    unless
        A command to run as a check, only run the named command if the command
        passed to the ``unless`` option returns false
    '''
    ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}

    # Check to make sure rev and mirror/bare are not both in use
    if rev and (mirror or bare):
        return _fail(ret, ('"rev" is not compatible with the "mirror" and '
                           '"bare" arguments'))

    if not target:
        return _fail(ret, '"target" option is required')
    if user is not None and runas is not None:
        # user wins over runas but let warn about the deprecation.
        ret.setdefault('warnings', []).append(
            'Passed both the \'runas\' and \'user\' arguments. Please don\'t. '
            '\'runas\' is being ignored in favor of \'user\'.')
        runas = None
    elif runas is not None:
        # Support old runas usage
        user = runas
        runas = None

    run_check_cmd_kwargs = {'runas': user}

    # check if git.latest should be applied
    cret = _run_check(run_check_cmd_kwargs, onlyif, unless)
    if isinstance(cret, dict):
        ret.update(cret)
        return ret

    bare = bare or mirror
    check = 'refs' if bare else '.git'

    if os.path.isdir(target) and os.path.isdir('{0}/{1}'.format(target,
                                                                check)):
        # git pull is probably required
        log.debug(('target {0} is found, "git pull" '
                   'is probably required'.format(target)))
        try:
            current_rev = __salt__['git.revision'](target, user=user)

            # handle the case where a branch was provided for rev
            remote_rev = None
            branch = __salt__['git.current_branch'](target, user=user)
            # We're only interested in the remote branch if a branch
            # (instead of a hash, for example) was provided for rev.
            if len(branch) > 0 and branch == rev:
                remote_rev = __salt__['git.ls_remote'](target,
                                                       repository=name,
                                                       branch=branch,
                                                       user=user,
                                                       identity=identity)

            # only do something, if the specified rev differs from the
            # current_rev and remote_rev
            if current_rev in [rev, remote_rev]:
                new_rev = current_rev
            else:

                if __opts__['test']:
                    return _neutral_test(
                        ret,
                        ('Repository {0} update is probably required (current '
                         'revision is {1})').format(target, current_rev))

                # if remote_name is defined set fetch_opts to remote_name
                if remote_name != 'origin':
                    fetch_opts = remote_name
                else:
                    fetch_opts = ''

                # check remote if fetch_url not == name set it
                remote = __salt__['git.remote_get'](target,
                                                    remote=remote_name,
                                                    user=user)
                if remote is None or remote[0] != name:
                    __salt__['git.remote_set'](target,
                                               remote=remote_name,
                                               url=name,
                                               user=user)
                    ret['changes']['remote/{0}'.format(
                        remote_name)] = "{0} => {1}".format(str(remote), name)

                # caheck if rev is already present in repo, git-fetch otherwise
                if bare:
                    __salt__['git.fetch'](target,
                                          opts=fetch_opts,
                                          user=user,
                                          identity=identity)
                elif rev:

                    cmd = "git rev-parse " + rev + '^{commit}'
                    retcode = __salt__['cmd.retcode'](cmd,
                                                      cwd=target,
                                                      user=user)
                    # there is a issues #3938 addressing this
                    if 0 != retcode or always_fetch:
                        __salt__['git.fetch'](target,
                                              opts=fetch_opts,
                                              user=user,
                                              identity=identity)

                    __salt__['git.checkout'](target,
                                             rev,
                                             force=force_checkout,
                                             user=user)

                # check if we are on a branch to merge changes
                cmd = "git symbolic-ref -q HEAD > /dev/null"
                retcode = __salt__['cmd.retcode'](cmd,
                                                  python_shell=True,
                                                  cwd=target,
                                                  user=user)
                # XXX: the real different with git.latest is here
                # we pull
                # but also fetch and merge --only-ff in case of errors
                if 0 == retcode:
                    method = 'git.fetch' if bare else 'git.pull'

                    def pull():
                        __salt__[method](target,
                                         opts=fetch_opts,
                                         user=user,
                                         identity=identity)

                    try:
                        pull()
                    except Exception, ex:
                        ex_msg = ex.message.lower()
                        # if the pb is the remote branch not being set
                        # just set it and run pull again
                        if (("--set-upstream" in ex_msg) and firstrun
                                and len(branch) > 0 and branch == rev):
                            cmd = 'git branch --set-upstream {branch} origin/{rev}'.format(
                                branch=branch,
                                rev=rev,
                            )
                            __salt__['cmd.run_stdout'](cmd,
                                                       cwd=target,
                                                       python_shell=True,
                                                       user=user)
                            pull()
                        else:
                            if ((method == 'git.fetch')
                                    or (not 'local changes' in ex_msg)):
                                raise
                            __salt__['git.fetch'](target,
                                                  opts=fetch_opts,
                                                  user=user,
                                                  identity=identity)
                            __salt__['git.fetch'](target,
                                                  opts='--tags',
                                                  user=user,
                                                  identity=identity)
                            __salt__['git.merge'](target,
                                                  opts='--ff-only',
                                                  user=user)
                if submodules:
                    __salt__['git.submodule'](target,
                                              user=user,
                                              identity=identity,
                                              opts='--recursive')

                new_rev = __salt__['git.revision'](cwd=target, user=user)
        except Exception as exc:
            return _fail(ret, str(exc))

        if current_rev != new_rev:
            log.info('Repository {0} updated: {1} => {2}'.format(
                target, current_rev, new_rev))
            ret['comment'] = 'Repository {0} updated'.format(target)
            ret['changes']['revision'] = '{0} => {1}'.format(
                current_rev, new_rev)
    else:
        # KISS: let do the rest of the job by original git.latest
        locs = globals()
        locs.update(locals())
        for i in [
                '__env__',
                '__grains__',
                '__lowstate__',
                '__opts__',
                '__package__',
                '__pillar__',
                '__running__',
                '__salt__',
        ]:
            if not hasattr(git, i):
                setattr(git, i, locs[i])
        return git.latest(name,
                          rev=rev,
                          target=target,
                          user=user,
                          force=force,
                          force_checkout=force_checkout,
                          submodules=submodules,
                          mirror=mirror,
                          bare=bare,
                          remote_name=remote_name,
                          always_fetch=always_fetch,
                          identity=identity,
                          onlyif=onlyif,
                          unless=unless)
    return ret