1"""
2Support for the Git SCM
3"""
4
5import copy
6import glob
7import logging
8import os
9import re
10import stat
11
12import salt.utils.args
13import salt.utils.data
14import salt.utils.files
15import salt.utils.functools
16import salt.utils.itertools
17import salt.utils.path
18import salt.utils.platform
19import salt.utils.stringutils
20import salt.utils.templates
21import salt.utils.url
22from salt.exceptions import CommandExecutionError, SaltInvocationError
23from salt.utils.versions import LooseVersion as _LooseVersion
24
25log = logging.getLogger(__name__)
26
27__func_alias__ = {"rm_": "rm"}
28
29
30def __virtual__():
31    """
32    Only load if git exists on the system
33    """
34    if salt.utils.path.which("git") is None:
35        return (False, "The git execution module cannot be loaded: git unavailable.")
36    else:
37        return True
38
39
40def _check_worktree_support(failhard=True):
41    """
42    Ensure that we don't try to operate on worktrees in git < 2.5.0.
43    """
44    git_version = version(versioninfo=False)
45    if _LooseVersion(git_version) < _LooseVersion("2.5.0"):
46        if failhard:
47            raise CommandExecutionError(
48                "Worktrees are only supported in git 2.5.0 and newer "
49                "(detected git version: {})".format(git_version)
50            )
51        return False
52    return True
53
54
55def _config_getter(
56    get_opt,
57    key,
58    value_regex=None,
59    cwd=None,
60    user=None,
61    password=None,
62    ignore_retcode=False,
63    output_encoding=None,
64    **kwargs
65):
66    """
67    Common code for config.get_* functions, builds and runs the git CLI command
68    and returns the result dict for the calling function to parse.
69    """
70    kwargs = salt.utils.args.clean_kwargs(**kwargs)
71    global_ = kwargs.pop("global", False)
72    if kwargs:
73        salt.utils.args.invalid_kwargs(kwargs)
74
75    if cwd is None:
76        if not global_:
77            raise SaltInvocationError("'cwd' argument required unless global=True")
78    else:
79        cwd = _expand_path(cwd, user)
80
81    if get_opt == "--get-regexp":
82        if value_regex is not None and not isinstance(value_regex, str):
83            value_regex = str(value_regex)
84    else:
85        # Ignore value_regex
86        value_regex = None
87
88    command = ["git", "config"]
89    command.extend(
90        _which_git_config(global_, cwd, user, password, output_encoding=output_encoding)
91    )
92    command.append(get_opt)
93    command.append(key)
94    if value_regex is not None:
95        command.append(value_regex)
96    return _git_run(
97        command,
98        cwd=cwd,
99        user=user,
100        password=password,
101        ignore_retcode=ignore_retcode,
102        failhard=False,
103        output_encoding=output_encoding,
104    )
105
106
107def _expand_path(cwd, user):
108    """
109    Expand home directory
110    """
111    try:
112        to_expand = "~" + user if user else "~"
113    except TypeError:
114        # Users should never be numeric but if we don't account for this then
115        # we're going to get a traceback if someone passes this invalid input.
116        to_expand = "~" + str(user) if user else "~"
117    try:
118        return os.path.join(os.path.expanduser(to_expand), cwd)
119    except AttributeError:
120        return os.path.join(os.path.expanduser(to_expand), str(cwd))
121
122
123def _path_is_executable_others(path):
124    """
125    Check every part of path for executable permission
126    """
127    prevpath = None
128    while path and path != prevpath:
129        try:
130            if not os.stat(path).st_mode & stat.S_IXOTH:
131                return False
132        except OSError:
133            return False
134        prevpath = path
135        path, _ = os.path.split(path)
136    return True
137
138
139def _format_opts(opts):
140    """
141    Common code to inspect opts and split them if necessary
142    """
143    if opts is None:
144        return []
145    elif isinstance(opts, list):
146        new_opts = []
147        for item in opts:
148            if isinstance(item, str):
149                new_opts.append(item)
150            else:
151                new_opts.append(str(item))
152        return new_opts
153    else:
154        if not isinstance(opts, str):
155            opts = [str(opts)]
156        else:
157            opts = salt.utils.args.shlex_split(opts)
158    opts = salt.utils.data.decode(opts)
159    try:
160        if opts[-1] == "--":
161            # Strip the '--' if it was passed at the end of the opts string,
162            # it'll be added back (if necessary) in the calling function.
163            # Putting this check here keeps it from having to be repeated every
164            # time _format_opts() is invoked.
165            return opts[:-1]
166    except IndexError:
167        pass
168    return opts
169
170
171def _format_git_opts(opts):
172    """
173    Do a version check and make sure that the installed version of git can
174    support git -c
175    """
176    if opts:
177        version_ = version(versioninfo=False)
178        if _LooseVersion(version_) < _LooseVersion("1.7.2"):
179            raise SaltInvocationError(
180                "git_opts is only supported for git versions >= 1.7.2 "
181                "(detected: {})".format(version_)
182            )
183    return _format_opts(opts)
184
185
186def _find_ssh_exe():
187    """
188    Windows only: search for Git's bundled ssh.exe in known locations
189    """
190    # Known locations for Git's ssh.exe in Windows
191    globmasks = [
192        os.path.join(
193            os.getenv("SystemDrive"),
194            os.sep,
195            "Program Files*",
196            "Git",
197            "usr",
198            "bin",
199            "ssh.exe",
200        ),
201        os.path.join(
202            os.getenv("SystemDrive"), os.sep, "Program Files*", "Git", "bin", "ssh.exe"
203        ),
204    ]
205    for globmask in globmasks:
206        ssh_exe = glob.glob(globmask)
207        if ssh_exe and os.path.isfile(ssh_exe[0]):
208            ret = ssh_exe[0]
209            break
210    else:
211        ret = None
212
213    return ret
214
215
216def _git_run(
217    command,
218    cwd=None,
219    user=None,
220    password=None,
221    identity=None,
222    ignore_retcode=False,
223    failhard=True,
224    redirect_stderr=False,
225    saltenv="base",
226    output_encoding=None,
227    **kwargs
228):
229    """
230    simple, throw an exception with the error message on an error return code.
231
232    this function may be moved to the command module, spliced with
233    'cmd.run_all', and used as an alternative to 'cmd.run_all'. Some
234    commands don't return proper retcodes, so this can't replace 'cmd.run_all'.
235    """
236    env = {}
237
238    if identity:
239        _salt_cli = __opts__.get("__cli", "")
240        errors = []
241        missing_keys = []
242
243        # if the statefile provides multiple identities, they need to be tried
244        # (but also allow a string instead of a list)
245        if not isinstance(identity, list):
246            # force it into a list
247            identity = [identity]
248
249        # try each of the identities, independently
250        tmp_identity_file = None
251        for id_file in identity:
252            if "salt://" in id_file:
253                with salt.utils.files.set_umask(0o077):
254                    tmp_identity_file = salt.utils.files.mkstemp()
255                    _id_file = id_file
256                    id_file = __salt__["cp.get_file"](
257                        id_file, tmp_identity_file, saltenv
258                    )
259                if not id_file:
260                    log.error("identity %s does not exist.", _id_file)
261                    __salt__["file.remove"](tmp_identity_file)
262                    continue
263                else:
264                    if user:
265                        os.chown(id_file, __salt__["file.user_to_uid"](user), -1)
266            else:
267                if not __salt__["file.file_exists"](id_file):
268                    missing_keys.append(id_file)
269                    log.error("identity %s does not exist.", id_file)
270                    continue
271
272            env = {"GIT_IDENTITY": id_file}
273
274            # copy wrapper to area accessible by ``runas`` user
275            # currently no support in windows for wrapping git ssh
276            ssh_id_wrapper = os.path.abspath(
277                os.path.join(
278                    salt.utils.templates.TEMPLATE_DIRNAME, "git/ssh-id-wrapper"
279                )
280            )
281            tmp_ssh_wrapper = None
282            if salt.utils.platform.is_windows():
283                ssh_exe = _find_ssh_exe()
284                if ssh_exe is None:
285                    raise CommandExecutionError(
286                        "Failed to find ssh.exe, unable to use identity file"
287                    )
288                env["GIT_SSH_EXE"] = ssh_exe
289                # Use the windows batch file instead of the bourne shell script
290                ssh_id_wrapper += ".bat"
291                env["GIT_SSH"] = ssh_id_wrapper
292            elif not user or _path_is_executable_others(ssh_id_wrapper):
293                env["GIT_SSH"] = ssh_id_wrapper
294            else:
295                tmp_ssh_wrapper = salt.utils.files.mkstemp()
296                salt.utils.files.copyfile(ssh_id_wrapper, tmp_ssh_wrapper)
297                os.chmod(tmp_ssh_wrapper, 0o500)
298                os.chown(tmp_ssh_wrapper, __salt__["file.user_to_uid"](user), -1)
299                env["GIT_SSH"] = tmp_ssh_wrapper
300
301            if "salt-call" not in _salt_cli and __utils__["ssh.key_is_encrypted"](
302                id_file
303            ):
304                errors.append(
305                    "Identity file {} is passphrase-protected and cannot be "
306                    "used in a non-interactive command. Using salt-call from "
307                    "the minion will allow a passphrase-protected key to be "
308                    "used.".format(id_file)
309                )
310                continue
311
312            log.info("Attempting git authentication using identity file %s", id_file)
313
314            try:
315                result = __salt__["cmd.run_all"](
316                    command,
317                    cwd=cwd,
318                    runas=user,
319                    password=password,
320                    env=env,
321                    python_shell=False,
322                    log_callback=salt.utils.url.redact_http_basic_auth,
323                    ignore_retcode=ignore_retcode,
324                    redirect_stderr=redirect_stderr,
325                    output_encoding=output_encoding,
326                    **kwargs
327                )
328            finally:
329                if tmp_ssh_wrapper:
330                    # Cleanup the temporary ssh wrapper file
331                    try:
332                        __salt__["file.remove"](tmp_ssh_wrapper)
333                        log.debug("Removed ssh wrapper file %s", tmp_ssh_wrapper)
334                    except AttributeError:
335                        # No wrapper was used
336                        pass
337                    except (SaltInvocationError, CommandExecutionError) as exc:
338                        log.warning(
339                            "Failed to remove ssh wrapper file %s: %s",
340                            tmp_ssh_wrapper,
341                            exc,
342                        )
343
344                if tmp_identity_file:
345                    # Cleanup the temporary identity file
346                    try:
347                        __salt__["file.remove"](tmp_identity_file)
348                        log.debug("Removed identity file %s", tmp_identity_file)
349                    except AttributeError:
350                        # No identify file was used
351                        pass
352                    except (SaltInvocationError, CommandExecutionError) as exc:
353                        log.warning(
354                            "Failed to remove identity file %s: %s",
355                            tmp_identity_file,
356                            exc,
357                        )
358
359            # If the command was successful, no need to try additional IDs
360            if result["retcode"] == 0:
361                return result
362            else:
363                err = result["stdout" if redirect_stderr else "stderr"]
364                if err:
365                    errors.append(salt.utils.url.redact_http_basic_auth(err))
366
367        # We've tried all IDs and still haven't passed, so error out
368        if failhard:
369            msg = "Unable to authenticate using identity file:\n\n{}".format(
370                "\n".join(errors)
371            )
372            if missing_keys:
373                if errors:
374                    msg += "\n\n"
375                msg += "The following identity file(s) were not found: {}".format(
376                    ", ".join(missing_keys)
377                )
378            raise CommandExecutionError(msg)
379        return result
380
381    else:
382        result = __salt__["cmd.run_all"](
383            command,
384            cwd=cwd,
385            runas=user,
386            password=password,
387            env=env,
388            python_shell=False,
389            log_callback=salt.utils.url.redact_http_basic_auth,
390            ignore_retcode=ignore_retcode,
391            redirect_stderr=redirect_stderr,
392            output_encoding=output_encoding,
393            **kwargs
394        )
395
396        if result["retcode"] == 0:
397            return result
398        else:
399            if failhard:
400                gitcommand = " ".join(command) if isinstance(command, list) else command
401                msg = "Command '{}' failed".format(
402                    salt.utils.url.redact_http_basic_auth(gitcommand)
403                )
404                err = result["stdout" if redirect_stderr else "stderr"]
405                if err:
406                    msg += ": {}".format(salt.utils.url.redact_http_basic_auth(err))
407                raise CommandExecutionError(msg)
408            return result
409
410
411def _get_toplevel(path, user=None, password=None, output_encoding=None):
412    """
413    Use git rev-parse to return the top level of a repo
414    """
415    return _git_run(
416        ["git", "rev-parse", "--show-toplevel"],
417        cwd=path,
418        user=user,
419        password=password,
420        output_encoding=output_encoding,
421    )["stdout"]
422
423
424def _git_config(cwd, user, password, output_encoding=None):
425    """
426    Helper to retrieve git config options
427    """
428    contextkey = "git.config." + cwd
429    if contextkey not in __context__:
430        git_dir = rev_parse(
431            cwd,
432            opts=["--git-dir"],
433            user=user,
434            password=password,
435            ignore_retcode=True,
436            output_encoding=output_encoding,
437        )
438        if not os.path.isabs(git_dir):
439            paths = (cwd, git_dir, "config")
440        else:
441            paths = (git_dir, "config")
442        __context__[contextkey] = os.path.join(*paths)
443    return __context__[contextkey]
444
445
446def _which_git_config(global_, cwd, user, password, output_encoding=None):
447    """
448    Based on whether global or local config is desired, return a list of CLI
449    args to include in the git config command.
450    """
451    if global_:
452        return ["--global"]
453    version_ = _LooseVersion(version(versioninfo=False))
454    if version_ >= _LooseVersion("1.7.10.2"):
455        # --local added in 1.7.10.2
456        return ["--local"]
457    else:
458        # For earlier versions, need to specify the path to the git config file
459        return [
460            "--file",
461            _git_config(cwd, user, password, output_encoding=output_encoding),
462        ]
463
464
465def add(
466    cwd,
467    filename,
468    opts="",
469    git_opts="",
470    user=None,
471    password=None,
472    ignore_retcode=False,
473    output_encoding=None,
474):
475    """
476    .. versionchanged:: 2015.8.0
477        The ``--verbose`` command line argument is now implied
478
479    Interface to `git-add(1)`_
480
481    cwd
482        The path to the git checkout
483
484    filename
485        The location of the file/directory to add, relative to ``cwd``
486
487    opts
488        Any additional options to add to the command line, in a single string
489
490        .. note::
491            On the Salt CLI, if the opts are preceded with a dash, it is
492            necessary to precede them with ``opts=`` (as in the CLI examples
493            below) to avoid causing errors with Salt's own argument parsing.
494
495    git_opts
496        Any additional options to add to git command itself (not the ``add``
497        subcommand), in a single string. This is useful for passing ``-c`` to
498        run git with temporary changes to the git configuration.
499
500        .. versionadded:: 2017.7.0
501
502        .. note::
503            This is only supported in git 1.7.2 and newer.
504
505    user
506        User under which to run the git command. By default, the command is run
507        by the user under which the minion is running.
508
509    password
510        Windows only. Required when specifying ``user``. This parameter will be
511        ignored on non-Windows platforms.
512
513      .. versionadded:: 2016.3.4
514
515    ignore_retcode : False
516        If ``True``, do not log an error to the minion log if the git command
517        returns a nonzero exit status.
518
519        .. versionadded:: 2015.8.0
520
521    output_encoding
522        Use this option to specify which encoding to use to decode the output
523        from any git commands which are run. This should not be needed in most
524        cases.
525
526        .. note::
527            This should only be needed if the files in the repository were
528            created with filenames using an encoding other than UTF-8 to handle
529            Unicode characters.
530
531        .. versionadded:: 2018.3.1
532
533    .. _`git-add(1)`: http://git-scm.com/docs/git-add
534
535    CLI Examples:
536
537    .. code-block:: bash
538
539        salt myminion git.add /path/to/repo foo/bar.py
540        salt myminion git.add /path/to/repo foo/bar.py opts='--dry-run'
541    """
542    cwd = _expand_path(cwd, user)
543    command = ["git"] + _format_git_opts(git_opts)
544    command.extend(["add", "--verbose"])
545    command.extend([x for x in _format_opts(opts) if x not in ("-v", "--verbose")])
546    command.extend(["--", filename])
547    return _git_run(
548        command,
549        cwd=cwd,
550        user=user,
551        password=password,
552        ignore_retcode=ignore_retcode,
553        output_encoding=output_encoding,
554    )["stdout"]
555
556
557def archive(
558    cwd,
559    output,
560    rev="HEAD",
561    prefix=None,
562    git_opts="",
563    user=None,
564    password=None,
565    ignore_retcode=False,
566    output_encoding=None,
567    **kwargs
568):
569    """
570    .. versionchanged:: 2015.8.0
571        Returns ``True`` if successful, raises an error if not.
572
573    Interface to `git-archive(1)`_, exports a tarball/zip file of the
574    repository
575
576    cwd
577        The path to be archived
578
579        .. note::
580            ``git archive`` permits a partial archive to be created. Thus, this
581            path does not need to be the root of the git repository. Only the
582            files within the directory specified by ``cwd`` (and its
583            subdirectories) will be in the resulting archive. For example, if
584            there is a git checkout at ``/tmp/foo``, then passing
585            ``/tmp/foo/bar`` as the ``cwd`` will result in just the files
586            underneath ``/tmp/foo/bar`` to be exported as an archive.
587
588    output
589        The path of the archive to be created
590
591    overwrite : False
592        Unless set to ``True``, Salt will over overwrite an existing archive at
593        the path specified by the ``output`` argument.
594
595        .. versionadded:: 2015.8.0
596
597    rev : HEAD
598        The revision from which to create the archive
599
600    format
601        Manually specify the file format of the resulting archive. This
602        argument can be omitted, and ``git archive`` will attempt to guess the
603        archive type (and compression) from the filename. ``zip``, ``tar``,
604        ``tar.gz``, and ``tgz`` are extensions that are recognized
605        automatically, and git can be configured to support other archive types
606        with the addition of git configuration keys.
607
608        See the `git-archive(1)`_ manpage explanation of the
609        ``--format`` argument (as well as the ``CONFIGURATION`` section of the
610        manpage) for further information.
611
612        .. versionadded:: 2015.8.0
613
614    prefix
615        Prepend ``<prefix>`` to every filename in the archive. If unspecified,
616        the name of the directory at the top level of the repository will be
617        used as the prefix (e.g. if ``cwd`` is set to ``/foo/bar/baz``, the
618        prefix will be ``baz``, and the resulting archive will contain a
619        top-level directory by that name).
620
621        .. note::
622            The default behavior if the ``--prefix`` option for ``git archive``
623            is not specified is to not prepend a prefix, so Salt's behavior
624            differs slightly from ``git archive`` in this respect. Use
625            ``prefix=''`` to create an archive with no prefix.
626
627        .. versionchanged:: 2015.8.0
628            The behavior of this argument has been changed slightly. As of
629            this version, it is necessary to include the trailing slash when
630            specifying a prefix, if the prefix is intended to create a
631            top-level directory.
632
633    git_opts
634        Any additional options to add to git command itself (not the
635        ``archive`` subcommand), in a single string. This is useful for passing
636        ``-c`` to run git with temporary changes to the git configuration.
637
638        .. versionadded:: 2017.7.0
639
640        .. note::
641            This is only supported in git 1.7.2 and newer.
642
643    user
644        User under which to run the git command. By default, the command is run
645        by the user under which the minion is running.
646
647    password
648        Windows only. Required when specifying ``user``. This parameter will be
649        ignored on non-Windows platforms.
650
651      .. versionadded:: 2016.3.4
652
653    ignore_retcode : False
654        If ``True``, do not log an error to the minion log if the git command
655        returns a nonzero exit status.
656
657        .. versionadded:: 2015.8.0
658
659    output_encoding
660        Use this option to specify which encoding to use to decode the output
661        from any git commands which are run. This should not be needed in most
662        cases.
663
664        .. note::
665            This should only be needed if the files in the repository were
666            created with filenames using an encoding other than UTF-8 to handle
667            Unicode characters.
668
669        .. versionadded:: 2018.3.1
670
671    .. _`git-archive(1)`: http://git-scm.com/docs/git-archive
672
673    CLI Example:
674
675    .. code-block:: bash
676
677        salt myminion git.archive /path/to/repo /path/to/archive.tar
678    """
679    cwd = _expand_path(cwd, user)
680    output = _expand_path(output, user)
681    # Sanitize kwargs and make sure that no invalid ones were passed. This
682    # allows us to accept 'format' as an argument to this function without
683    # shadowing the format() global, while also not allowing unwanted arguments
684    # to be passed.
685    kwargs = salt.utils.args.clean_kwargs(**kwargs)
686    format_ = kwargs.pop("format", None)
687    if kwargs:
688        salt.utils.args.invalid_kwargs(kwargs)
689
690    command = ["git"] + _format_git_opts(git_opts)
691    command.append("archive")
692    # If prefix was set to '' then we skip adding the --prefix option, but if
693    # it was not passed (i.e. None) we use the cwd.
694    if prefix != "":
695        if not prefix:
696            prefix = os.path.basename(cwd) + os.sep
697        command.extend(["--prefix", prefix])
698
699    if format_:
700        command.extend(["--format", format_])
701    command.extend(["--output", output, rev])
702    _git_run(
703        command,
704        cwd=cwd,
705        user=user,
706        password=password,
707        ignore_retcode=ignore_retcode,
708        output_encoding=output_encoding,
709    )
710    # No output (unless --verbose is used, and we don't want all files listed
711    # in the output in case there are thousands), so just return True. If there
712    # was an error in the git command, it will have already raised an exception
713    # and we will never get to this return statement.
714    return True
715
716
717def branch(
718    cwd,
719    name=None,
720    opts="",
721    git_opts="",
722    user=None,
723    password=None,
724    ignore_retcode=False,
725    output_encoding=None,
726):
727    """
728    Interface to `git-branch(1)`_
729
730    cwd
731        The path to the git checkout
732
733    name
734        Name of the branch on which to operate. If not specified, the current
735        branch will be assumed.
736
737    opts
738        Any additional options to add to the command line, in a single string
739
740        .. note::
741            To create a branch based on something other than HEAD, pass the
742            name of the revision as ``opts``. If the revision is in the format
743            ``remotename/branch``, then this will also set the remote tracking
744            branch.
745
746            Additionally, on the Salt CLI, if the opts are preceded with a
747            dash, it is necessary to precede them with ``opts=`` (as in the CLI
748            examples below) to avoid causing errors with Salt's own argument
749            parsing.
750
751    git_opts
752        Any additional options to add to git command itself (not the ``branch``
753        subcommand), in a single string. This is useful for passing ``-c`` to
754        run git with temporary changes to the git configuration.
755
756        .. versionadded:: 2017.7.0
757
758        .. note::
759            This is only supported in git 1.7.2 and newer.
760
761    user
762        User under which to run the git command. By default, the command is run
763        by the user under which the minion is running.
764
765    password
766        Windows only. Required when specifying ``user``. This parameter will be
767        ignored on non-Windows platforms.
768
769      .. versionadded:: 2016.3.4
770
771    ignore_retcode : False
772        If ``True``, do not log an error to the minion log if the git command
773        returns a nonzero exit status.
774
775        .. versionadded:: 2015.8.0
776
777    output_encoding
778        Use this option to specify which encoding to use to decode the output
779        from any git commands which are run. This should not be needed in most
780        cases.
781
782        .. note::
783            This should only be needed if the files in the repository were
784            created with filenames using an encoding other than UTF-8 to handle
785            Unicode characters.
786
787        .. versionadded:: 2018.3.1
788
789    .. _`git-branch(1)`: http://git-scm.com/docs/git-branch
790
791    CLI Examples:
792
793    .. code-block:: bash
794
795        # Set remote tracking branch
796        salt myminion git.branch /path/to/repo mybranch opts='--set-upstream-to origin/mybranch'
797        # Create new branch
798        salt myminion git.branch /path/to/repo mybranch upstream/somebranch
799        # Delete branch
800        salt myminion git.branch /path/to/repo mybranch opts='-d'
801        # Rename branch (2015.8.0 and later)
802        salt myminion git.branch /path/to/repo newbranch opts='-m oldbranch'
803    """
804    cwd = _expand_path(cwd, user)
805    command = ["git"] + _format_git_opts(git_opts)
806    command.append("branch")
807    command.extend(_format_opts(opts))
808    if name is not None:
809        command.append(name)
810    _git_run(
811        command,
812        cwd=cwd,
813        user=user,
814        password=password,
815        ignore_retcode=ignore_retcode,
816        output_encoding=output_encoding,
817    )
818    return True
819
820
821def checkout(
822    cwd,
823    rev=None,
824    force=False,
825    opts="",
826    git_opts="",
827    user=None,
828    password=None,
829    ignore_retcode=False,
830    output_encoding=None,
831):
832    """
833    Interface to `git-checkout(1)`_
834
835    cwd
836        The path to the git checkout
837
838    opts
839        Any additional options to add to the command line, in a single string
840
841        .. note::
842            On the Salt CLI, if the opts are preceded with a dash, it is
843            necessary to precede them with ``opts=`` (as in the CLI examples
844            below) to avoid causing errors with Salt's own argument parsing.
845
846    git_opts
847        Any additional options to add to git command itself (not the
848        ``checkout`` subcommand), in a single string. This is useful for
849        passing ``-c`` to run git with temporary changes to the git
850        configuration.
851
852        .. versionadded:: 2017.7.0
853
854        .. note::
855            This is only supported in git 1.7.2 and newer.
856
857    rev
858        The remote branch or revision to checkout.
859
860        .. versionchanged:: 2015.8.0
861            Optional when using ``-b`` or ``-B`` in ``opts``.
862
863    force : False
864        Force a checkout even if there might be overwritten changes
865
866    user
867        User under which to run the git command. By default, the command is run
868        by the user under which the minion is running.
869
870    password
871        Windows only. Required when specifying ``user``. This parameter will be
872        ignored on non-Windows platforms.
873
874      .. versionadded:: 2016.3.4
875
876    ignore_retcode : False
877        If ``True``, do not log an error to the minion log if the git command
878        returns a nonzero exit status.
879
880        .. versionadded:: 2015.8.0
881
882    output_encoding
883        Use this option to specify which encoding to use to decode the output
884        from any git commands which are run. This should not be needed in most
885        cases.
886
887        .. note::
888            This should only be needed if the files in the repository were
889            created with filenames using an encoding other than UTF-8 to handle
890            Unicode characters.
891
892        .. versionadded:: 2018.3.1
893
894    .. _`git-checkout(1)`: http://git-scm.com/docs/git-checkout
895
896    CLI Examples:
897
898    .. code-block:: bash
899
900        # Checking out local local revisions
901        salt myminion git.checkout /path/to/repo somebranch user=jeff
902        salt myminion git.checkout /path/to/repo opts='testbranch -- conf/file1 file2'
903        salt myminion git.checkout /path/to/repo rev=origin/mybranch opts='--track'
904        # Checking out remote revision into new branch
905        salt myminion git.checkout /path/to/repo upstream/master opts='-b newbranch'
906        # Checking out current revision into new branch (2015.8.0 and later)
907        salt myminion git.checkout /path/to/repo opts='-b newbranch'
908    """
909    cwd = _expand_path(cwd, user)
910    command = ["git"] + _format_git_opts(git_opts)
911    command.append("checkout")
912    if force:
913        command.append("--force")
914    opts = _format_opts(opts)
915    command.extend(opts)
916    checkout_branch = any(x in opts for x in ("-b", "-B"))
917    if rev is None:
918        if not checkout_branch:
919            raise SaltInvocationError(
920                "'rev' argument is required unless -b or -B in opts"
921            )
922    else:
923        command.append(rev)
924    # Checkout message goes to stderr
925    return _git_run(
926        command,
927        cwd=cwd,
928        user=user,
929        password=password,
930        ignore_retcode=ignore_retcode,
931        redirect_stderr=True,
932        output_encoding=output_encoding,
933    )["stdout"]
934
935
936def clone(
937    cwd,
938    url=None,  # Remove default value once 'repository' arg is removed
939    name=None,
940    opts="",
941    git_opts="",
942    user=None,
943    password=None,
944    identity=None,
945    https_user=None,
946    https_pass=None,
947    ignore_retcode=False,
948    saltenv="base",
949    output_encoding=None,
950):
951    """
952    Interface to `git-clone(1)`_
953
954    cwd
955        Location of git clone
956
957        .. versionchanged:: 2015.8.0
958            If ``name`` is passed, then the clone will be made *within* this
959            directory.
960
961    url
962        The URL of the repository to be cloned
963
964        .. versionchanged:: 2015.8.0
965            Argument renamed from ``repository`` to ``url``
966
967    name
968        Optional alternate name for the top-level directory to be created by
969        the clone
970
971        .. versionadded:: 2015.8.0
972
973    opts
974        Any additional options to add to the command line, in a single string
975
976    git_opts
977        Any additional options to add to git command itself (not the ``clone``
978        subcommand), in a single string. This is useful for passing ``-c`` to
979        run git with temporary changes to the git configuration.
980
981        .. versionadded:: 2017.7.0
982
983        .. note::
984            This is only supported in git 1.7.2 and newer.
985
986    user
987        User under which to run the git command. By default, the command is run
988        by the user under which the minion is running.
989
990    password
991        Windows only. Required when specifying ``user``. This parameter will be
992        ignored on non-Windows platforms.
993
994      .. versionadded:: 2016.3.4
995
996    identity
997        Path to a private key to use for ssh URLs
998
999        .. warning::
1000
1001            Unless Salt is invoked from the minion using ``salt-call``, the
1002            key(s) must be passphraseless. For greater security with
1003            passphraseless private keys, see the `sshd(8)`_ manpage for
1004            information on securing the keypair from the remote side in the
1005            ``authorized_keys`` file.
1006
1007            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
1008
1009        .. versionchanged:: 2015.8.7
1010
1011            Salt will no longer attempt to use passphrase-protected keys unless
1012            invoked from the minion using ``salt-call``, to prevent blocking
1013            waiting for user input.
1014
1015        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
1016
1017        .. versionchanged:: 2016.3.0
1018
1019    https_user
1020        Set HTTP Basic Auth username. Only accepted for HTTPS URLs.
1021
1022        .. versionadded:: 20515.5.0
1023
1024    https_pass
1025        Set HTTP Basic Auth password. Only accepted for HTTPS URLs.
1026
1027        .. versionadded:: 2015.5.0
1028
1029    ignore_retcode : False
1030        If ``True``, do not log an error to the minion log if the git command
1031        returns a nonzero exit status.
1032
1033        .. versionadded:: 2015.8.0
1034
1035    saltenv
1036        The default salt environment to pull sls files from
1037
1038        .. versionadded:: 2016.3.1
1039
1040    output_encoding
1041        Use this option to specify which encoding to use to decode the output
1042        from any git commands which are run. This should not be needed in most
1043        cases.
1044
1045        .. note::
1046            This should only be needed if the files in the repository were
1047            created with filenames using an encoding other than UTF-8 to handle
1048            Unicode characters.
1049
1050        .. versionadded:: 2018.3.1
1051
1052    .. _`git-clone(1)`: http://git-scm.com/docs/git-clone
1053
1054    CLI Example:
1055
1056    .. code-block:: bash
1057
1058        salt myminion git.clone /path/to/repo_parent_dir git://github.com/saltstack/salt.git
1059    """
1060    cwd = _expand_path(cwd, user)
1061
1062    if not url:
1063        raise SaltInvocationError("Missing 'url' argument")
1064
1065    try:
1066        url = salt.utils.url.add_http_basic_auth(
1067            url, https_user, https_pass, https_only=True
1068        )
1069    except ValueError as exc:
1070        raise SaltInvocationError(exc.__str__())
1071
1072    command = ["git"] + _format_git_opts(git_opts)
1073    command.append("clone")
1074    command.extend(_format_opts(opts))
1075    command.extend(["--", url])
1076    if name is not None:
1077        command.append(name)
1078        if not os.path.exists(cwd):
1079            os.makedirs(cwd)
1080        clone_cwd = cwd
1081    else:
1082        command.append(cwd)
1083        # Use '/tmp' instead of $HOME (/root for root user) to work around
1084        # upstream git bug. See the following comment on the Salt bug tracker
1085        # for more info:
1086        # https://github.com/saltstack/salt/issues/15519#issuecomment-128531310
1087        # On Windows, just fall back to None (runs git clone command using the
1088        # home directory as the cwd).
1089        clone_cwd = "/tmp" if not salt.utils.platform.is_windows() else None
1090    _git_run(
1091        command,
1092        cwd=clone_cwd,
1093        user=user,
1094        password=password,
1095        identity=identity,
1096        ignore_retcode=ignore_retcode,
1097        saltenv=saltenv,
1098        output_encoding=output_encoding,
1099    )
1100    return True
1101
1102
1103def commit(
1104    cwd,
1105    message,
1106    opts="",
1107    git_opts="",
1108    user=None,
1109    password=None,
1110    filename=None,
1111    ignore_retcode=False,
1112    output_encoding=None,
1113):
1114    """
1115    Interface to `git-commit(1)`_
1116
1117    cwd
1118        The path to the git checkout
1119
1120    message
1121        Commit message
1122
1123    opts
1124        Any additional options to add to the command line, in a single string.
1125        These opts will be added to the end of the git command being run.
1126
1127        .. note::
1128            On the Salt CLI, if the opts are preceded with a dash, it is
1129            necessary to precede them with ``opts=`` (as in the CLI examples
1130            below) to avoid causing errors with Salt's own argument parsing.
1131
1132            The ``-m`` option should not be passed here, as the commit message
1133            will be defined by the ``message`` argument.
1134
1135    git_opts
1136        Any additional options to add to git command itself (not the ``commit``
1137        subcommand), in a single string. This is useful for passing ``-c`` to
1138        run git with temporary changes to the git configuration.
1139
1140        .. versionadded:: 2017.7.0
1141
1142        .. note::
1143            This is only supported in git 1.7.2 and newer.
1144
1145    user
1146        User under which to run the git command. By default, the command is run
1147        by the user under which the minion is running.
1148
1149    password
1150        Windows only. Required when specifying ``user``. This parameter will be
1151        ignored on non-Windows platforms.
1152
1153      .. versionadded:: 2016.3.4
1154
1155    filename
1156        The location of the file/directory to commit, relative to ``cwd``.
1157        This argument is optional, and can be used to commit a file without
1158        first staging it.
1159
1160        .. note::
1161            This argument only works on files which are already tracked by the
1162            git repository.
1163
1164        .. versionadded:: 2015.8.0
1165
1166    ignore_retcode : False
1167        If ``True``, do not log an error to the minion log if the git command
1168        returns a nonzero exit status.
1169
1170        .. versionadded:: 2015.8.0
1171
1172    output_encoding
1173        Use this option to specify which encoding to use to decode the output
1174        from any git commands which are run. This should not be needed in most
1175        cases.
1176
1177        .. note::
1178            This should only be needed if the files in the repository were
1179            created with filenames using an encoding other than UTF-8 to handle
1180            Unicode characters.
1181
1182        .. versionadded:: 2018.3.1
1183
1184    .. _`git-commit(1)`: http://git-scm.com/docs/git-commit
1185
1186    CLI Examples:
1187
1188    .. code-block:: bash
1189
1190        salt myminion git.commit /path/to/repo 'The commit message'
1191        salt myminion git.commit /path/to/repo 'The commit message' filename=foo/bar.py
1192    """
1193    cwd = _expand_path(cwd, user)
1194    command = ["git"] + _format_git_opts(git_opts)
1195    command.extend(["commit", "-m", message])
1196    command.extend(_format_opts(opts))
1197    if filename:
1198        # Add the '--' to terminate CLI args, but only if it wasn't already
1199        # passed in opts string.
1200        command.extend(["--", filename])
1201    return _git_run(
1202        command,
1203        cwd=cwd,
1204        user=user,
1205        password=password,
1206        ignore_retcode=ignore_retcode,
1207        output_encoding=output_encoding,
1208    )["stdout"]
1209
1210
1211def config_get(
1212    key,
1213    cwd=None,
1214    user=None,
1215    password=None,
1216    ignore_retcode=False,
1217    output_encoding=None,
1218    **kwargs
1219):
1220    """
1221    Get the value of a key in the git configuration file
1222
1223    key
1224        The name of the configuration key to get
1225
1226        .. versionchanged:: 2015.8.0
1227            Argument renamed from ``setting_name`` to ``key``
1228
1229    cwd
1230        The path to the git checkout
1231
1232        .. versionchanged:: 2015.8.0
1233            Now optional if ``global`` is set to ``True``
1234
1235    global : False
1236        If ``True``, query the global git configuration. Otherwise, only the
1237        local git configuration will be queried.
1238
1239        .. versionadded:: 2015.8.0
1240
1241    all : False
1242        If ``True``, return a list of all values set for ``key``. If the key
1243        does not exist, ``None`` will be returned.
1244
1245        .. versionadded:: 2015.8.0
1246
1247    user
1248        User under which to run the git command. By default, the command is run
1249        by the user under which the minion is running.
1250
1251    password
1252        Windows only. Required when specifying ``user``. This parameter will be
1253        ignored on non-Windows platforms.
1254
1255      .. versionadded:: 2016.3.4
1256
1257    ignore_retcode : False
1258        If ``True``, do not log an error to the minion log if the git command
1259        returns a nonzero exit status.
1260
1261        .. versionadded:: 2015.8.0
1262
1263    output_encoding
1264        Use this option to specify which encoding to use to decode the output
1265        from any git commands which are run. This should not be needed in most
1266        cases.
1267
1268        .. note::
1269            This should only be needed if the files in the repository were
1270            created with filenames using an encoding other than UTF-8 to handle
1271            Unicode characters.
1272
1273        .. versionadded:: 2018.3.1
1274
1275    CLI Examples:
1276
1277    .. code-block:: bash
1278
1279        salt myminion git.config_get user.name cwd=/path/to/repo
1280        salt myminion git.config_get user.email global=True
1281        salt myminion git.config_get core.gitproxy cwd=/path/to/repo all=True
1282    """
1283    # Sanitize kwargs and make sure that no invalid ones were passed. This
1284    # allows us to accept 'all' as an argument to this function without
1285    # shadowing all(), while also not allowing unwanted arguments to be passed.
1286    all_ = kwargs.pop("all", False)
1287
1288    result = _config_getter(
1289        "--get-all",
1290        key,
1291        cwd=cwd,
1292        user=user,
1293        password=password,
1294        ignore_retcode=ignore_retcode,
1295        output_encoding=output_encoding,
1296        **kwargs
1297    )
1298
1299    # git config --get exits with retcode of 1 when key does not exist
1300    if result["retcode"] == 1:
1301        return None
1302    ret = result["stdout"].splitlines()
1303    if all_:
1304        return ret
1305    else:
1306        try:
1307            return ret[-1]
1308        except IndexError:
1309            # Should never happen but I'm paranoid and don't like tracebacks
1310            return ""
1311
1312
1313def config_get_regexp(
1314    key,
1315    value_regex=None,
1316    cwd=None,
1317    user=None,
1318    password=None,
1319    ignore_retcode=False,
1320    output_encoding=None,
1321    **kwargs
1322):
1323    r"""
1324    .. versionadded:: 2015.8.0
1325
1326    Get the value of a key or keys in the git configuration file using regexes
1327    for more flexible matching. The return data is a dictionary mapping keys to
1328    lists of values matching the ``value_regex``. If no values match, an empty
1329    dictionary will be returned.
1330
1331    key
1332        Regex on which key names will be matched
1333
1334    value_regex
1335        If specified, return all values matching this regex. The return data
1336        will be a dictionary mapping keys to lists of values matching the
1337        regex.
1338
1339        .. important::
1340            Only values matching the ``value_regex`` will be part of the return
1341            data. So, if ``key`` matches a multivar, then it is possible that
1342            not all of the values will be returned. To get all values set for a
1343            multivar, simply omit the ``value_regex`` argument.
1344
1345    cwd
1346        The path to the git checkout
1347
1348    global : False
1349        If ``True``, query the global git configuration. Otherwise, only the
1350        local git configuration will be queried.
1351
1352    user
1353        User under which to run the git command. By default, the command is run
1354        by the user under which the minion is running.
1355
1356    password
1357        Windows only. Required when specifying ``user``. This parameter will be
1358        ignored on non-Windows platforms.
1359
1360      .. versionadded:: 2016.3.4
1361
1362    ignore_retcode : False
1363        If ``True``, do not log an error to the minion log if the git command
1364        returns a nonzero exit status.
1365
1366    output_encoding
1367        Use this option to specify which encoding to use to decode the output
1368        from any git commands which are run. This should not be needed in most
1369        cases.
1370
1371        .. note::
1372            This should only be needed if the files in the repository were
1373            created with filenames using an encoding other than UTF-8 to handle
1374            Unicode characters.
1375
1376        .. versionadded:: 2018.3.1
1377
1378    CLI Examples:
1379
1380    .. code-block:: bash
1381
1382        # Matches any values for key 'foo.bar'
1383        salt myminion git.config_get_regexp /path/to/repo foo.bar
1384        # Matches any value starting with 'baz' set for key 'foo.bar'
1385        salt myminion git.config_get_regexp /path/to/repo foo.bar 'baz.*'
1386        # Matches any key starting with 'user.'
1387        salt myminion git.config_get_regexp '^user\.' global=True
1388    """
1389    result = _config_getter(
1390        "--get-regexp",
1391        key,
1392        value_regex=value_regex,
1393        cwd=cwd,
1394        user=user,
1395        password=password,
1396        ignore_retcode=ignore_retcode,
1397        output_encoding=output_encoding,
1398        **kwargs
1399    )
1400
1401    # git config --get exits with retcode of 1 when key does not exist
1402    ret = {}
1403    if result["retcode"] == 1:
1404        return ret
1405    for line in result["stdout"].splitlines():
1406        try:
1407            param, value = line.split(None, 1)
1408        except ValueError:
1409            continue
1410        ret.setdefault(param, []).append(value)
1411    return ret
1412
1413
1414config_get_regex = salt.utils.functools.alias_function(
1415    config_get_regexp, "config_get_regex"
1416)
1417
1418
1419def config_set(
1420    key,
1421    value=None,
1422    multivar=None,
1423    cwd=None,
1424    user=None,
1425    password=None,
1426    ignore_retcode=False,
1427    output_encoding=None,
1428    **kwargs
1429):
1430    """
1431    .. versionchanged:: 2015.8.0
1432        Return the value(s) of the key being set
1433
1434    Set a key in the git configuration file
1435
1436    cwd
1437        The path to the git checkout. Must be an absolute path, or the word
1438        ``global`` to indicate that a global key should be set.
1439
1440        .. versionchanged:: 2014.7.0
1441            Made ``cwd`` argument optional if ``is_global=True``
1442
1443    key
1444        The name of the configuration key to set
1445
1446        .. versionchanged:: 2015.8.0
1447            Argument renamed from ``setting_name`` to ``key``
1448
1449    value
1450        The value to set for the specified key. Incompatible with the
1451        ``multivar`` argument.
1452
1453        .. versionchanged:: 2015.8.0
1454            Argument renamed from ``setting_value`` to ``value``
1455
1456    add : False
1457        Add a value to a key, creating/updating a multivar
1458
1459        .. versionadded:: 2015.8.0
1460
1461    multivar
1462        Set a multivar all at once. Values can be comma-separated or passed as
1463        a Python list. Incompatible with the ``value`` argument.
1464
1465        .. versionadded:: 2015.8.0
1466
1467    user
1468        User under which to run the git command. By default, the command is run
1469        by the user under which the minion is running.
1470
1471    password
1472        Windows only. Required when specifying ``user``. This parameter will be
1473        ignored on non-Windows platforms.
1474
1475      .. versionadded:: 2016.3.4
1476
1477    ignore_retcode : False
1478        If ``True``, do not log an error to the minion log if the git command
1479        returns a nonzero exit status.
1480
1481        .. versionadded:: 2015.8.0
1482
1483    global : False
1484        If ``True``, set a global variable
1485
1486    output_encoding
1487        Use this option to specify which encoding to use to decode the output
1488        from any git commands which are run. This should not be needed in most
1489        cases.
1490
1491        .. note::
1492            This should only be needed if the files in the repository were
1493            created with filenames using an encoding other than UTF-8 to handle
1494            Unicode characters.
1495
1496        .. versionadded:: 2018.3.1
1497
1498    CLI Examples:
1499
1500    .. code-block:: bash
1501
1502        salt myminion git.config_set user.email me@example.com cwd=/path/to/repo
1503        salt myminion git.config_set user.email foo@bar.com global=True
1504    """
1505    kwargs = salt.utils.args.clean_kwargs(**kwargs)
1506    add_ = kwargs.pop("add", False)
1507    global_ = kwargs.pop("global", False)
1508    if kwargs:
1509        salt.utils.args.invalid_kwargs(kwargs)
1510
1511    if cwd is None:
1512        if not global_:
1513            raise SaltInvocationError("'cwd' argument required unless global=True")
1514    else:
1515        cwd = _expand_path(cwd, user)
1516
1517    if all(x is not None for x in (value, multivar)):
1518        raise SaltInvocationError("Only one of 'value' and 'multivar' is permitted")
1519
1520    if multivar is not None:
1521        if not isinstance(multivar, list):
1522            try:
1523                multivar = multivar.split(",")
1524            except AttributeError:
1525                multivar = str(multivar).split(",")
1526        else:
1527            new_multivar = []
1528            for item in salt.utils.data.decode(multivar):
1529                if isinstance(item, str):
1530                    new_multivar.append(item)
1531                else:
1532                    new_multivar.append(str(item))
1533            multivar = new_multivar
1534
1535    command_prefix = ["git", "config"]
1536    if global_:
1537        command_prefix.append("--global")
1538
1539    if value is not None:
1540        command = copy.copy(command_prefix)
1541        if add_:
1542            command.append("--add")
1543        else:
1544            command.append("--replace-all")
1545        command.extend([key, value])
1546        _git_run(
1547            command,
1548            cwd=cwd,
1549            user=user,
1550            password=password,
1551            ignore_retcode=ignore_retcode,
1552            output_encoding=output_encoding,
1553        )
1554    else:
1555        for idx, target in enumerate(multivar):
1556            command = copy.copy(command_prefix)
1557            if idx == 0:
1558                command.append("--replace-all")
1559            else:
1560                command.append("--add")
1561            command.extend([key, target])
1562            _git_run(
1563                command,
1564                cwd=cwd,
1565                user=user,
1566                password=password,
1567                ignore_retcode=ignore_retcode,
1568                output_encoding=output_encoding,
1569            )
1570    return config_get(
1571        key,
1572        user=user,
1573        password=password,
1574        cwd=cwd,
1575        ignore_retcode=ignore_retcode,
1576        output_encoding=output_encoding,
1577        **{"all": True, "global": global_}
1578    )
1579
1580
1581def config_unset(
1582    key,
1583    value_regex=None,
1584    cwd=None,
1585    user=None,
1586    password=None,
1587    ignore_retcode=False,
1588    output_encoding=None,
1589    **kwargs
1590):
1591    """
1592    .. versionadded:: 2015.8.0
1593
1594    Unset a key in the git configuration file
1595
1596    cwd
1597        The path to the git checkout. Must be an absolute path, or the word
1598        ``global`` to indicate that a global key should be unset.
1599
1600    key
1601        The name of the configuration key to unset
1602
1603    value_regex
1604        Regular expression that matches exactly one key, used to delete a
1605        single value from a multivar. Ignored if ``all`` is set to ``True``.
1606
1607    all : False
1608        If ``True`` unset all values for a multivar. If ``False``, and ``key``
1609        is a multivar, an error will be raised.
1610
1611    global : False
1612        If ``True``, unset set a global variable. Otherwise, a local variable
1613        will be unset.
1614
1615    user
1616        User under which to run the git command. By default, the command is run
1617        by the user under which the minion is running.
1618
1619    password
1620        Windows only. Required when specifying ``user``. This parameter will be
1621        ignored on non-Windows platforms.
1622
1623      .. versionadded:: 2016.3.4
1624
1625    ignore_retcode : False
1626        If ``True``, do not log an error to the minion log if the git command
1627        returns a nonzero exit status.
1628
1629    output_encoding
1630        Use this option to specify which encoding to use to decode the output
1631        from any git commands which are run. This should not be needed in most
1632        cases.
1633
1634        .. note::
1635            This should only be needed if the files in the repository were
1636            created with filenames using an encoding other than UTF-8 to handle
1637            Unicode characters.
1638
1639        .. versionadded:: 2018.3.1
1640
1641    CLI Example:
1642
1643    .. code-block:: bash
1644
1645        salt myminion git.config_unset /path/to/repo foo.bar
1646        salt myminion git.config_unset /path/to/repo foo.bar all=True
1647    """
1648    kwargs = salt.utils.args.clean_kwargs(**kwargs)
1649    all_ = kwargs.pop("all", False)
1650    global_ = kwargs.pop("global", False)
1651    if kwargs:
1652        salt.utils.args.invalid_kwargs(kwargs)
1653
1654    if cwd is None:
1655        if not global_:
1656            raise SaltInvocationError("'cwd' argument required unless global=True")
1657    else:
1658        cwd = _expand_path(cwd, user)
1659
1660    command = ["git", "config"]
1661    if all_:
1662        command.append("--unset-all")
1663    else:
1664        command.append("--unset")
1665    command.extend(
1666        _which_git_config(global_, cwd, user, password, output_encoding=output_encoding)
1667    )
1668
1669    command.append(key)
1670    if value_regex is not None:
1671        command.append(value_regex)
1672    ret = _git_run(
1673        command,
1674        cwd=cwd if cwd != "global" else None,
1675        user=user,
1676        password=password,
1677        ignore_retcode=ignore_retcode,
1678        failhard=False,
1679        output_encoding=output_encoding,
1680    )
1681    retcode = ret["retcode"]
1682    if retcode == 0:
1683        return True
1684    elif retcode == 1:
1685        raise CommandExecutionError("Section or key is invalid")
1686    elif retcode == 5:
1687        if (
1688            config_get(
1689                key,
1690                cwd=cwd,
1691                user=user,
1692                password=password,
1693                ignore_retcode=ignore_retcode,
1694                output_encoding=output_encoding,
1695            )
1696            is None
1697        ):
1698            raise CommandExecutionError("Key '{}' does not exist".format(key))
1699        else:
1700            msg = "Multiple values exist for key '{}'".format(key)
1701            if value_regex is not None:
1702                msg += " and value_regex matches multiple values"
1703            raise CommandExecutionError(msg)
1704    elif retcode == 6:
1705        raise CommandExecutionError("The value_regex is invalid")
1706    else:
1707        msg = "Failed to unset key '{}', git config returned exit code {}".format(
1708            key, retcode
1709        )
1710        if ret["stderr"]:
1711            msg += "; " + ret["stderr"]
1712        raise CommandExecutionError(msg)
1713
1714
1715def current_branch(
1716    cwd, user=None, password=None, ignore_retcode=False, output_encoding=None
1717):
1718    """
1719    Returns the current branch name of a local checkout. If HEAD is detached,
1720    return the SHA1 of the revision which is currently checked out.
1721
1722    cwd
1723        The path to the git checkout
1724
1725    user
1726        User under which to run the git command. By default, the command is run
1727        by the user under which the minion is running.
1728
1729    password
1730        Windows only. Required when specifying ``user``. This parameter will be
1731        ignored on non-Windows platforms.
1732
1733      .. versionadded:: 2016.3.4
1734
1735    ignore_retcode : False
1736        If ``True``, do not log an error to the minion log if the git command
1737        returns a nonzero exit status.
1738
1739        .. versionadded:: 2015.8.0
1740
1741    output_encoding
1742        Use this option to specify which encoding to use to decode the output
1743        from any git commands which are run. This should not be needed in most
1744        cases.
1745
1746        .. note::
1747            This should only be needed if the files in the repository were
1748            created with filenames using an encoding other than UTF-8 to handle
1749            Unicode characters.
1750
1751        .. versionadded:: 2018.3.1
1752
1753    CLI Example:
1754
1755    .. code-block:: bash
1756
1757        salt myminion git.current_branch /path/to/repo
1758    """
1759    cwd = _expand_path(cwd, user)
1760    command = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
1761    return _git_run(
1762        command,
1763        cwd=cwd,
1764        user=user,
1765        password=password,
1766        ignore_retcode=ignore_retcode,
1767        output_encoding=output_encoding,
1768    )["stdout"]
1769
1770
1771def describe(
1772    cwd,
1773    rev="HEAD",
1774    user=None,
1775    password=None,
1776    ignore_retcode=False,
1777    output_encoding=None,
1778):
1779    """
1780    Returns the `git-describe(1)`_ string (or the SHA1 hash if there are no
1781    tags) for the given revision.
1782
1783    cwd
1784        The path to the git checkout
1785
1786    rev : HEAD
1787        The revision to describe
1788
1789    user
1790        User under which to run the git command. By default, the command is run
1791        by the user under which the minion is running.
1792
1793    password
1794        Windows only. Required when specifying ``user``. This parameter will be
1795        ignored on non-Windows platforms.
1796
1797      .. versionadded:: 2016.3.4
1798
1799    ignore_retcode : False
1800        If ``True``, do not log an error to the minion log if the git command
1801        returns a nonzero exit status.
1802
1803        .. versionadded:: 2015.8.0
1804
1805    output_encoding
1806        Use this option to specify which encoding to use to decode the output
1807        from any git commands which are run. This should not be needed in most
1808        cases.
1809
1810        .. note::
1811            This should only be needed if the files in the repository were
1812            created with filenames using an encoding other than UTF-8 to handle
1813            Unicode characters.
1814
1815        .. versionadded:: 2018.3.1
1816
1817    .. _`git-describe(1)`: http://git-scm.com/docs/git-describe
1818
1819    CLI Examples:
1820
1821    .. code-block:: bash
1822
1823        salt myminion git.describe /path/to/repo
1824        salt myminion git.describe /path/to/repo develop
1825    """
1826    cwd = _expand_path(cwd, user)
1827    command = ["git", "describe"]
1828    if _LooseVersion(version(versioninfo=False)) >= _LooseVersion("1.5.6"):
1829        command.append("--always")
1830    command.append(rev)
1831    return _git_run(
1832        command,
1833        cwd=cwd,
1834        user=user,
1835        password=password,
1836        ignore_retcode=ignore_retcode,
1837        output_encoding=output_encoding,
1838    )["stdout"]
1839
1840
1841def diff(
1842    cwd,
1843    item1=None,
1844    item2=None,
1845    opts="",
1846    git_opts="",
1847    user=None,
1848    password=None,
1849    no_index=False,
1850    cached=False,
1851    paths=None,
1852    output_encoding=None,
1853):
1854    """
1855    .. versionadded:: 2015.8.12,2016.3.3,2016.11.0
1856
1857    Interface to `git-diff(1)`_
1858
1859    cwd
1860        The path to the git checkout
1861
1862    item1 and item2
1863        Revision(s) to pass to the ``git diff`` command. One or both of these
1864        arguments may be ignored if some of the options below are set to
1865        ``True``. When ``cached`` is ``False``, and no revisions are passed
1866        to this function, then the current working tree will be compared
1867        against the index (i.e. unstaged changes). When two revisions are
1868        passed, they will be compared to each other.
1869
1870    opts
1871        Any additional options to add to the command line, in a single string
1872
1873        .. note::
1874            On the Salt CLI, if the opts are preceded with a dash, it is
1875            necessary to precede them with ``opts=`` (as in the CLI examples
1876            below) to avoid causing errors with Salt's own argument parsing.
1877
1878    git_opts
1879        Any additional options to add to git command itself (not the ``diff``
1880        subcommand), in a single string. This is useful for passing ``-c`` to
1881        run git with temporary changes to the git configuration.
1882
1883        .. versionadded:: 2017.7.0
1884
1885        .. note::
1886            This is only supported in git 1.7.2 and newer.
1887
1888    user
1889        User under which to run the git command. By default, the command is run
1890        by the user under which the minion is running.
1891
1892    password
1893        Windows only. Required when specifying ``user``. This parameter will be
1894        ignored on non-Windows platforms.
1895
1896      .. versionadded:: 2016.3.4
1897
1898    no_index : False
1899        When it is necessary to diff two files in the same repo against each
1900        other, and not diff two different revisions, set this option to
1901        ``True``. If this is left ``False`` in these instances, then a normal
1902        ``git diff`` will be performed against the index (i.e. unstaged
1903        changes), and files in the ``paths`` option will be used to narrow down
1904        the diff output.
1905
1906        .. note::
1907            Requires Git 1.5.1 or newer. Additionally, when set to ``True``,
1908            ``item1`` and ``item2`` will be ignored.
1909
1910    cached : False
1911        If ``True``, compare staged changes to ``item1`` (if specified),
1912        otherwise compare them to the most recent commit.
1913
1914        .. note::
1915            ``item2`` is ignored if this option is is set to ``True``.
1916
1917    paths
1918        File paths to pass to the ``git diff`` command. Can be passed as a
1919        comma-separated list or a Python list.
1920
1921    output_encoding
1922        Use this option to specify which encoding to use to decode the output
1923        from any git commands which are run. This should not be needed in most
1924        cases.
1925
1926        .. note::
1927            This should only be needed if the files in the repository were
1928            created with filenames using an encoding other than UTF-8 to handle
1929            Unicode characters.
1930
1931        .. versionadded:: 2018.3.1
1932
1933    .. _`git-diff(1)`: http://git-scm.com/docs/git-diff
1934
1935    CLI Example:
1936
1937    .. code-block:: bash
1938
1939        # Perform diff against the index (staging area for next commit)
1940        salt myminion git.diff /path/to/repo
1941        # Compare staged changes to the most recent commit
1942        salt myminion git.diff /path/to/repo cached=True
1943        # Compare staged changes to a specific revision
1944        salt myminion git.diff /path/to/repo mybranch cached=True
1945        # Perform diff against the most recent commit (includes staged changes)
1946        salt myminion git.diff /path/to/repo HEAD
1947        # Diff two commits
1948        salt myminion git.diff /path/to/repo abcdef1 aabbccd
1949        # Diff two commits, only showing differences in the specified paths
1950        salt myminion git.diff /path/to/repo abcdef1 aabbccd paths=path/to/file1,path/to/file2
1951        # Diff two files with one being outside the working tree
1952        salt myminion git.diff /path/to/repo no_index=True paths=path/to/file1,/absolute/path/to/file2
1953    """
1954    if no_index and cached:
1955        raise CommandExecutionError(
1956            "The 'no_index' and 'cached' options cannot be used together"
1957        )
1958
1959    command = ["git"] + _format_git_opts(git_opts)
1960    command.append("diff")
1961    command.extend(_format_opts(opts))
1962
1963    if paths is not None and not isinstance(paths, (list, tuple)):
1964        try:
1965            paths = paths.split(",")
1966        except AttributeError:
1967            paths = str(paths).split(",")
1968
1969    ignore_retcode = False
1970    failhard = True
1971
1972    if no_index:
1973        if _LooseVersion(version(versioninfo=False)) < _LooseVersion("1.5.1"):
1974            raise CommandExecutionError(
1975                "The 'no_index' option is only supported in Git 1.5.1 and newer"
1976            )
1977        ignore_retcode = True
1978        failhard = False
1979        command.append("--no-index")
1980        for value in [x for x in (item1, item2) if x]:
1981            log.warning(
1982                "Revision '%s' ignored in git diff, as revisions cannot be "
1983                "used when no_index=True",
1984                value,
1985            )
1986
1987    elif cached:
1988        command.append("--cached")
1989        if item1:
1990            command.append(item1)
1991        if item2:
1992            log.warning(
1993                "Second revision '%s' ignored in git diff, at most one "
1994                "revision is considered when cached=True",
1995                item2,
1996            )
1997
1998    else:
1999        for value in [x for x in (item1, item2) if x]:
2000            command.append(value)
2001
2002    if paths:
2003        command.append("--")
2004        command.extend(paths)
2005
2006    return _git_run(
2007        command,
2008        cwd=cwd,
2009        user=user,
2010        password=password,
2011        ignore_retcode=ignore_retcode,
2012        failhard=failhard,
2013        redirect_stderr=True,
2014        output_encoding=output_encoding,
2015    )["stdout"]
2016
2017
2018def discard_local_changes(
2019    cwd, path=".", user=None, password=None, ignore_retcode=False, output_encoding=None
2020):
2021    """
2022    .. versionadded:: 2019.2.0
2023
2024    Runs a ``git checkout -- <path>`` from the directory specified by ``cwd``.
2025
2026    cwd
2027        The path to the git checkout
2028
2029    path
2030        path relative to cwd (defaults to ``.``)
2031
2032    user
2033        User under which to run the git command. By default, the command is run
2034        by the user under which the minion is running.
2035
2036    password
2037        Windows only. Required when specifying ``user``. This parameter will be
2038        ignored on non-Windows platforms.
2039
2040    ignore_retcode : False
2041        If ``True``, do not log an error to the minion log if the git command
2042        returns a nonzero exit status.
2043
2044    output_encoding
2045        Use this option to specify which encoding to use to decode the output
2046        from any git commands which are run. This should not be needed in most
2047        cases.
2048
2049        .. note::
2050            This should only be needed if the files in the repository were
2051            created with filenames using an encoding other than UTF-8 to handle
2052            Unicode characters.
2053
2054    CLI Example:
2055
2056    .. code-block:: bash
2057
2058        salt myminion git.discard_local_changes /path/to/repo
2059        salt myminion git.discard_local_changes /path/to/repo path=foo
2060    """
2061    cwd = _expand_path(cwd, user)
2062    command = ["git", "checkout", "--", path]
2063    # Checkout message goes to stderr
2064    return _git_run(
2065        command,
2066        cwd=cwd,
2067        user=user,
2068        password=password,
2069        ignore_retcode=ignore_retcode,
2070        redirect_stderr=True,
2071        output_encoding=output_encoding,
2072    )["stdout"]
2073
2074
2075def fetch(
2076    cwd,
2077    remote=None,
2078    force=False,
2079    refspecs=None,
2080    opts="",
2081    git_opts="",
2082    user=None,
2083    password=None,
2084    identity=None,
2085    ignore_retcode=False,
2086    saltenv="base",
2087    output_encoding=None,
2088):
2089    """
2090    .. versionchanged:: 2015.8.2
2091        Return data is now a dictionary containing information on branches and
2092        tags that were added/updated
2093
2094    Interface to `git-fetch(1)`_
2095
2096    cwd
2097        The path to the git checkout
2098
2099    remote
2100        Optional remote name to fetch. If not passed, then git will use its
2101        default behavior (as detailed in `git-fetch(1)`_).
2102
2103        .. versionadded:: 2015.8.0
2104
2105    force
2106        Force the fetch even when it is not a fast-forward.
2107
2108        .. versionadded:: 2015.8.0
2109
2110    refspecs
2111        Override the refspec(s) configured for the remote with this argument.
2112        Multiple refspecs can be passed, comma-separated.
2113
2114        .. versionadded:: 2015.8.0
2115
2116    opts
2117        Any additional options to add to the command line, in a single string
2118
2119        .. note::
2120            On the Salt CLI, if the opts are preceded with a dash, it is
2121            necessary to precede them with ``opts=`` (as in the CLI examples
2122            below) to avoid causing errors with Salt's own argument parsing.
2123
2124    git_opts
2125        Any additional options to add to git command itself (not the ``fetch``
2126        subcommand), in a single string. This is useful for passing ``-c`` to
2127        run git with temporary changes to the git configuration.
2128
2129        .. versionadded:: 2017.7.0
2130
2131        .. note::
2132            This is only supported in git 1.7.2 and newer.
2133
2134    user
2135        User under which to run the git command. By default, the command is run
2136        by the user under which the minion is running.
2137
2138    password
2139        Windows only. Required when specifying ``user``. This parameter will be
2140        ignored on non-Windows platforms.
2141
2142      .. versionadded:: 2016.3.4
2143
2144    identity
2145        Path to a private key to use for ssh URLs
2146
2147        .. warning::
2148
2149            Unless Salt is invoked from the minion using ``salt-call``, the
2150            key(s) must be passphraseless. For greater security with
2151            passphraseless private keys, see the `sshd(8)`_ manpage for
2152            information on securing the keypair from the remote side in the
2153            ``authorized_keys`` file.
2154
2155            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
2156
2157        .. versionchanged:: 2015.8.7
2158
2159            Salt will no longer attempt to use passphrase-protected keys unless
2160            invoked from the minion using ``salt-call``, to prevent blocking
2161            waiting for user input.
2162
2163        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
2164
2165        .. versionchanged:: 2016.3.0
2166
2167    ignore_retcode : False
2168        If ``True``, do not log an error to the minion log if the git command
2169        returns a nonzero exit status.
2170
2171        .. versionadded:: 2015.8.0
2172
2173    saltenv
2174        The default salt environment to pull sls files from
2175
2176        .. versionadded:: 2016.3.1
2177
2178    output_encoding
2179        Use this option to specify which encoding to use to decode the output
2180        from any git commands which are run. This should not be needed in most
2181        cases.
2182
2183        .. note::
2184            This should only be needed if the files in the repository were
2185            created with filenames using an encoding other than UTF-8 to handle
2186            Unicode characters.
2187
2188        .. versionadded:: 2018.3.1
2189
2190    .. _`git-fetch(1)`: http://git-scm.com/docs/git-fetch
2191
2192    CLI Example:
2193
2194    .. code-block:: bash
2195
2196        salt myminion git.fetch /path/to/repo upstream
2197        salt myminion git.fetch /path/to/repo identity=/root/.ssh/id_rsa
2198    """
2199    cwd = _expand_path(cwd, user)
2200    command = ["git"] + _format_git_opts(git_opts)
2201    command.append("fetch")
2202    if force:
2203        command.append("--force")
2204    command.extend([x for x in _format_opts(opts) if x not in ("-f", "--force")])
2205    if remote:
2206        command.append(remote)
2207    if refspecs is not None:
2208        if not isinstance(refspecs, (list, tuple)):
2209            try:
2210                refspecs = refspecs.split(",")
2211            except AttributeError:
2212                refspecs = str(refspecs).split(",")
2213        refspecs = salt.utils.data.stringify(refspecs)
2214        command.extend(refspecs)
2215    output = _git_run(
2216        command,
2217        cwd=cwd,
2218        user=user,
2219        password=password,
2220        identity=identity,
2221        ignore_retcode=ignore_retcode,
2222        redirect_stderr=True,
2223        saltenv=saltenv,
2224        output_encoding=output_encoding,
2225    )["stdout"]
2226
2227    update_re = re.compile(
2228        r"[\s*]*(?:([0-9a-f]+)\.\.([0-9a-f]+)|"
2229        r"\[(?:new (tag|branch)|tag update)\])\s+(.+)->"
2230    )
2231    ret = {}
2232    for line in salt.utils.itertools.split(output, "\n"):
2233        match = update_re.match(line)
2234        if match:
2235            old_sha, new_sha, new_ref_type, ref_name = match.groups()
2236            ref_name = ref_name.rstrip()
2237            if new_ref_type is not None:
2238                # ref is a new tag/branch
2239                ref_key = "new tags" if new_ref_type == "tag" else "new branches"
2240                ret.setdefault(ref_key, []).append(ref_name)
2241            elif old_sha is not None:
2242                # ref is a branch update
2243                ret.setdefault("updated branches", {})[ref_name] = {
2244                    "old": old_sha,
2245                    "new": new_sha,
2246                }
2247            else:
2248                # ref is an updated tag
2249                ret.setdefault("updated tags", []).append(ref_name)
2250    return ret
2251
2252
2253def init(
2254    cwd,
2255    bare=False,
2256    template=None,
2257    separate_git_dir=None,
2258    shared=None,
2259    opts="",
2260    git_opts="",
2261    user=None,
2262    password=None,
2263    ignore_retcode=False,
2264    output_encoding=None,
2265):
2266    """
2267    Interface to `git-init(1)`_
2268
2269    cwd
2270        The path to the directory to be initialized
2271
2272    bare : False
2273        If ``True``, init a bare repository
2274
2275        .. versionadded:: 2015.8.0
2276
2277    template
2278        Set this argument to specify an alternate `template directory`_
2279
2280        .. versionadded:: 2015.8.0
2281
2282    separate_git_dir
2283        Set this argument to specify an alternate ``$GIT_DIR``
2284
2285        .. versionadded:: 2015.8.0
2286
2287    shared
2288        Set sharing permissions on git repo. See `git-init(1)`_ for more
2289        details.
2290
2291        .. versionadded:: 2015.8.0
2292
2293    opts
2294        Any additional options to add to the command line, in a single string
2295
2296        .. note::
2297            On the Salt CLI, if the opts are preceded with a dash, it is
2298            necessary to precede them with ``opts=`` (as in the CLI examples
2299            below) to avoid causing errors with Salt's own argument parsing.
2300
2301    git_opts
2302        Any additional options to add to git command itself (not the ``init``
2303        subcommand), in a single string. This is useful for passing ``-c`` to
2304        run git with temporary changes to the git configuration.
2305
2306        .. versionadded:: 2017.7.0
2307
2308        .. note::
2309            This is only supported in git 1.7.2 and newer.
2310
2311    user
2312        User under which to run the git command. By default, the command is run
2313        by the user under which the minion is running.
2314
2315    password
2316        Windows only. Required when specifying ``user``. This parameter will be
2317        ignored on non-Windows platforms.
2318
2319      .. versionadded:: 2016.3.4
2320
2321    ignore_retcode : False
2322        If ``True``, do not log an error to the minion log if the git command
2323        returns a nonzero exit status.
2324
2325        .. versionadded:: 2015.8.0
2326
2327    output_encoding
2328        Use this option to specify which encoding to use to decode the output
2329        from any git commands which are run. This should not be needed in most
2330        cases.
2331
2332        .. note::
2333            This should only be needed if the files in the repository were
2334            created with filenames using an encoding other than UTF-8 to handle
2335            Unicode characters.
2336
2337        .. versionadded:: 2018.3.1
2338
2339    .. _`git-init(1)`: http://git-scm.com/docs/git-init
2340    .. _`template directory`: http://git-scm.com/docs/git-init#_template_directory
2341
2342    CLI Examples:
2343
2344    .. code-block:: bash
2345
2346        salt myminion git.init /path/to/repo
2347        # Init a bare repo (before 2015.8.0)
2348        salt myminion git.init /path/to/bare/repo.git opts='--bare'
2349        # Init a bare repo (2015.8.0 and later)
2350        salt myminion git.init /path/to/bare/repo.git bare=True
2351    """
2352    cwd = _expand_path(cwd, user)
2353    command = ["git"] + _format_git_opts(git_opts)
2354    command.append("init")
2355    if bare:
2356        command.append("--bare")
2357    if template is not None:
2358        command.append("--template={}".format(template))
2359    if separate_git_dir is not None:
2360        command.append("--separate-git-dir={}".format(separate_git_dir))
2361    if shared is not None:
2362        if isinstance(shared, int) and not isinstance(shared, bool):
2363            shared = "0" + str(shared)
2364        elif not isinstance(shared, str):
2365            # Using lower here because booleans would be capitalized when
2366            # converted to a string.
2367            shared = str(shared).lower()
2368        command.append("--shared={}".format(shared))
2369    command.extend(_format_opts(opts))
2370    command.append(cwd)
2371    return _git_run(
2372        command,
2373        user=user,
2374        password=password,
2375        ignore_retcode=ignore_retcode,
2376        output_encoding=output_encoding,
2377    )["stdout"]
2378
2379
2380def is_worktree(cwd, user=None, password=None, output_encoding=None):
2381    """
2382    .. versionadded:: 2015.8.0
2383
2384    This function will attempt to determine if ``cwd`` is part of a
2385    worktree by checking its ``.git`` to see if it is a file containing a
2386    reference to another gitdir.
2387
2388    cwd
2389        path to the worktree to be removed
2390
2391    user
2392        User under which to run the git command. By default, the command is run
2393        by the user under which the minion is running.
2394
2395    password
2396        Windows only. Required when specifying ``user``. This parameter will be
2397        ignored on non-Windows platforms.
2398
2399      .. versionadded:: 2016.3.4
2400
2401    output_encoding
2402        Use this option to specify which encoding to use to decode the output
2403        from any git commands which are run. This should not be needed in most
2404        cases.
2405
2406        .. note::
2407            This should only be needed if the files in the repository were
2408            created with filenames using an encoding other than UTF-8 to handle
2409            Unicode characters.
2410
2411        .. versionadded:: 2018.3.1
2412
2413    CLI Example:
2414
2415    .. code-block:: bash
2416
2417        salt myminion git.is_worktree /path/to/repo
2418    """
2419    cwd = _expand_path(cwd, user)
2420    try:
2421        toplevel = _get_toplevel(
2422            cwd, user=user, password=password, output_encoding=output_encoding
2423        )
2424    except CommandExecutionError:
2425        return False
2426    gitdir = os.path.join(toplevel, ".git")
2427    try:
2428        with salt.utils.files.fopen(gitdir, "r") as fp_:
2429            for line in fp_:
2430                line = salt.utils.stringutils.to_unicode(line)
2431                try:
2432                    label, path = line.split(None, 1)
2433                except ValueError:
2434                    return False
2435                else:
2436                    # This file should only contain a single line. However, we
2437                    # loop here to handle the corner case where .git is a large
2438                    # binary file, so that we do not read the entire file into
2439                    # memory at once. We'll hit a return statement before this
2440                    # loop enters a second iteration.
2441                    if label == "gitdir:" and os.path.isabs(path):
2442                        return True
2443                    else:
2444                        return False
2445    except OSError:
2446        return False
2447    return False
2448
2449
2450def list_branches(
2451    cwd,
2452    remote=False,
2453    user=None,
2454    password=None,
2455    ignore_retcode=False,
2456    output_encoding=None,
2457):
2458    """
2459    .. versionadded:: 2015.8.0
2460
2461    Return a list of branches
2462
2463    cwd
2464        The path to the git checkout
2465
2466    remote : False
2467        If ``True``, list remote branches. Otherwise, local branches will be
2468        listed.
2469
2470        .. warning::
2471
2472            This option will only return remote branches of which the local
2473            checkout is aware, use :py:func:`git.fetch
2474            <salt.modules.git.fetch>` to update remotes.
2475
2476    user
2477        User under which to run the git command. By default, the command is run
2478        by the user under which the minion is running.
2479
2480    password
2481        Windows only. Required when specifying ``user``. This parameter will be
2482        ignored on non-Windows platforms.
2483
2484      .. versionadded:: 2016.3.4
2485
2486    ignore_retcode : False
2487        If ``True``, do not log an error to the minion log if the git command
2488        returns a nonzero exit status.
2489
2490        .. versionadded:: 2015.8.0
2491
2492    output_encoding
2493        Use this option to specify which encoding to use to decode the output
2494        from any git commands which are run. This should not be needed in most
2495        cases.
2496
2497        .. note::
2498            This should only be needed if the files in the repository were
2499            created with filenames using an encoding other than UTF-8 to handle
2500            Unicode characters.
2501
2502        .. versionadded:: 2018.3.1
2503
2504    CLI Examples:
2505
2506    .. code-block:: bash
2507
2508        salt myminion git.list_branches /path/to/repo
2509        salt myminion git.list_branches /path/to/repo remote=True
2510    """
2511    cwd = _expand_path(cwd, user)
2512    command = [
2513        "git",
2514        "for-each-ref",
2515        "--format",
2516        "%(refname:short)",
2517        "refs/{}/".format("heads" if not remote else "remotes"),
2518    ]
2519    return _git_run(
2520        command,
2521        cwd=cwd,
2522        user=user,
2523        password=password,
2524        ignore_retcode=ignore_retcode,
2525        output_encoding=output_encoding,
2526    )["stdout"].splitlines()
2527
2528
2529def list_tags(
2530    cwd, user=None, password=None, ignore_retcode=False, output_encoding=None
2531):
2532    """
2533    .. versionadded:: 2015.8.0
2534
2535    Return a list of tags
2536
2537    cwd
2538        The path to the git checkout
2539
2540    user
2541        User under which to run the git command. By default, the command is run
2542        by the user under which the minion is running.
2543
2544    password
2545        Windows only. Required when specifying ``user``. This parameter will be
2546        ignored on non-Windows platforms.
2547
2548      .. versionadded:: 2016.3.4
2549
2550    ignore_retcode : False
2551        If ``True``, do not log an error to the minion log if the git command
2552        returns a nonzero exit status.
2553
2554        .. versionadded:: 2015.8.0
2555
2556    output_encoding
2557        Use this option to specify which encoding to use to decode the output
2558        from any git commands which are run. This should not be needed in most
2559        cases.
2560
2561        .. note::
2562            This should only be needed if the files in the repository were
2563            created with filenames using an encoding other than UTF-8 to handle
2564            Unicode characters.
2565
2566        .. versionadded:: 2018.3.1
2567
2568    CLI Examples:
2569
2570    .. code-block:: bash
2571
2572        salt myminion git.list_tags /path/to/repo
2573    """
2574    cwd = _expand_path(cwd, user)
2575    command = ["git", "for-each-ref", "--format", "%(refname:short)", "refs/tags/"]
2576    return _git_run(
2577        command,
2578        cwd=cwd,
2579        user=user,
2580        password=password,
2581        ignore_retcode=ignore_retcode,
2582        output_encoding=output_encoding,
2583    )["stdout"].splitlines()
2584
2585
2586def list_worktrees(
2587    cwd, stale=False, user=None, password=None, output_encoding=None, **kwargs
2588):
2589    """
2590    .. versionadded:: 2015.8.0
2591
2592    Returns information on worktrees
2593
2594    .. versionchanged:: 2015.8.4
2595        Version 2.7.0 added the ``list`` subcommand to `git-worktree(1)`_ which
2596        provides a lot of additional information. The return data has been
2597        changed to include this information, even for pre-2.7.0 versions of
2598        git. In addition, if a worktree has a detached head, then any tags
2599        which point to the worktree's HEAD will be included in the return data.
2600
2601    .. note::
2602        By default, only worktrees for which the worktree directory is still
2603        present are returned, but this can be changed using the ``all`` and
2604        ``stale`` arguments (described below).
2605
2606    cwd
2607        The path to the git checkout
2608
2609    user
2610        User under which to run the git command. By default, the command is run
2611        by the user under which the minion is running.
2612
2613    password
2614        Windows only. Required when specifying ``user``. This parameter will be
2615        ignored on non-Windows platforms.
2616
2617      .. versionadded:: 2016.3.4
2618
2619    all : False
2620        If ``True``, then return all worktrees tracked under
2621        $GIT_DIR/worktrees, including ones for which the gitdir is no longer
2622        present.
2623
2624    stale : False
2625        If ``True``, return *only* worktrees whose gitdir is no longer present.
2626
2627    .. note::
2628        Only one of ``all`` and ``stale`` can be set to ``True``.
2629
2630    output_encoding
2631        Use this option to specify which encoding to use to decode the output
2632        from any git commands which are run. This should not be needed in most
2633        cases.
2634
2635        .. note::
2636            This should only be needed if the files in the repository were
2637            created with filenames using an encoding other than UTF-8 to handle
2638            Unicode characters.
2639
2640        .. versionadded:: 2018.3.1
2641
2642    .. _`git-worktree(1)`: http://git-scm.com/docs/git-worktree
2643
2644    CLI Examples:
2645
2646    .. code-block:: bash
2647
2648        salt myminion git.list_worktrees /path/to/repo
2649        salt myminion git.list_worktrees /path/to/repo all=True
2650        salt myminion git.list_worktrees /path/to/repo stale=True
2651    """
2652    if not _check_worktree_support(failhard=True):
2653        return {}
2654    cwd = _expand_path(cwd, user)
2655    kwargs = salt.utils.args.clean_kwargs(**kwargs)
2656    all_ = kwargs.pop("all", False)
2657    if kwargs:
2658        salt.utils.args.invalid_kwargs(kwargs)
2659
2660    if all_ and stale:
2661        raise CommandExecutionError("'all' and 'stale' cannot both be set to True")
2662
2663    def _git_tag_points_at(cwd, rev, user=None, password=None, output_encoding=None):
2664        """
2665        Get any tags that point at a
2666        """
2667        return _git_run(
2668            ["git", "tag", "--points-at", rev],
2669            cwd=cwd,
2670            user=user,
2671            password=password,
2672            output_encoding=output_encoding,
2673        )["stdout"].splitlines()
2674
2675    def _desired(is_stale, all_, stale):
2676        """
2677        Common logic to determine whether or not to include the worktree info
2678        in the return data.
2679        """
2680        if is_stale:
2681            if not all_ and not stale:
2682                # Stale worktrees are not desired, skip this one
2683                return False
2684        else:
2685            if stale:
2686                # Only stale worktrees are desired, skip this one
2687                return False
2688        return True
2689
2690    def _duplicate_worktree_path(path):
2691        """
2692        Log errors to the minion log notifying of duplicate worktree paths.
2693        These should not be there, but may show up due to a bug in git 2.7.0.
2694        """
2695        log.error(
2696            "git.worktree: Duplicate worktree path %s. This may be caused by "
2697            "a known issue in git 2.7.0 (see "
2698            "http://permalink.gmane.org/gmane.comp.version-control.git/283998)",
2699            path,
2700        )
2701
2702    tracked_data_points = ("worktree", "HEAD", "branch")
2703    ret = {}
2704    git_version = _LooseVersion(version(versioninfo=False))
2705    has_native_list_subcommand = git_version >= _LooseVersion("2.7.0")
2706    if has_native_list_subcommand:
2707        out = _git_run(
2708            ["git", "worktree", "list", "--porcelain"],
2709            cwd=cwd,
2710            user=user,
2711            password=password,
2712            output_encoding=output_encoding,
2713        )
2714        if out["retcode"] != 0:
2715            msg = "Failed to list worktrees"
2716            if out["stderr"]:
2717                msg += ": {}".format(out["stderr"])
2718            raise CommandExecutionError(msg)
2719
2720        def _untracked_item(line):
2721            """
2722            Log a warning
2723            """
2724            log.warning("git.worktree: Untracked line item '%s'", line)
2725
2726        for individual_worktree in salt.utils.itertools.split(
2727            out["stdout"].strip(), "\n\n"
2728        ):
2729            # Initialize the dict where we're storing the tracked data points
2730            worktree_data = {x: "" for x in tracked_data_points}
2731
2732            for line in salt.utils.itertools.split(individual_worktree, "\n"):
2733                try:
2734                    type_, value = line.strip().split(None, 1)
2735                except ValueError:
2736                    if line == "detached":
2737                        type_ = "branch"
2738                        value = "detached"
2739                    else:
2740                        _untracked_item(line)
2741                        continue
2742
2743                if type_ not in tracked_data_points:
2744                    _untracked_item(line)
2745                    continue
2746
2747                if worktree_data[type_]:
2748                    log.error(
2749                        "git.worktree: Unexpected duplicate %s entry '%s', skipping",
2750                        type_,
2751                        line,
2752                    )
2753                    continue
2754
2755                worktree_data[type_] = value
2756
2757            # Check for missing data points
2758            missing = [x for x in tracked_data_points if not worktree_data[x]]
2759            if missing:
2760                log.error(
2761                    "git.worktree: Incomplete worktree data, missing the "
2762                    "following information: %s. Full data below:\n%s",
2763                    ", ".join(missing),
2764                    individual_worktree,
2765                )
2766                continue
2767
2768            worktree_is_stale = not os.path.isdir(worktree_data["worktree"])
2769
2770            if not _desired(worktree_is_stale, all_, stale):
2771                continue
2772
2773            if worktree_data["worktree"] in ret:
2774                _duplicate_worktree_path(worktree_data["worktree"])
2775
2776            wt_ptr = ret.setdefault(worktree_data["worktree"], {})
2777            wt_ptr["stale"] = worktree_is_stale
2778            wt_ptr["HEAD"] = worktree_data["HEAD"]
2779            wt_ptr["detached"] = worktree_data["branch"] == "detached"
2780            if wt_ptr["detached"]:
2781                wt_ptr["branch"] = None
2782                # Check to see if HEAD points at a tag
2783                tags_found = _git_tag_points_at(
2784                    cwd,
2785                    wt_ptr["HEAD"],
2786                    user=user,
2787                    password=password,
2788                    output_encoding=output_encoding,
2789                )
2790                if tags_found:
2791                    wt_ptr["tags"] = tags_found
2792            else:
2793                wt_ptr["branch"] = worktree_data["branch"].replace("refs/heads/", "", 1)
2794
2795        return ret
2796
2797    else:
2798        toplevel = _get_toplevel(
2799            cwd, user=user, password=password, output_encoding=output_encoding
2800        )
2801        try:
2802            worktree_root = rev_parse(
2803                cwd,
2804                opts=["--git-path", "worktrees"],
2805                user=user,
2806                password=password,
2807                output_encoding=output_encoding,
2808            )
2809        except CommandExecutionError as exc:
2810            msg = "Failed to find worktree location for " + cwd
2811            log.error(msg, exc_info_on_loglevel=logging.DEBUG)
2812            raise CommandExecutionError(msg)
2813        if worktree_root.startswith(".git"):
2814            worktree_root = os.path.join(cwd, worktree_root)
2815        if not os.path.isdir(worktree_root):
2816            raise CommandExecutionError(
2817                "Worktree admin directory {} not present".format(worktree_root)
2818            )
2819
2820        def _read_file(path):
2821            """
2822            Return contents of a single line file with EOF newline stripped
2823            """
2824            try:
2825                with salt.utils.files.fopen(path, "r") as fp_:
2826                    for line in fp_:
2827                        ret = salt.utils.stringutils.to_unicode(line).strip()
2828                        # Ignore other lines, if they exist (which they
2829                        # shouldn't)
2830                        break
2831                    return ret
2832            except OSError as exc:
2833                # Raise a CommandExecutionError
2834                salt.utils.files.process_read_exception(exc, path)
2835
2836        for worktree_name in os.listdir(worktree_root):
2837            admin_dir = os.path.join(worktree_root, worktree_name)
2838            gitdir_file = os.path.join(admin_dir, "gitdir")
2839            head_file = os.path.join(admin_dir, "HEAD")
2840
2841            wt_loc = _read_file(gitdir_file)
2842            head_ref = _read_file(head_file)
2843
2844            if not os.path.isabs(wt_loc):
2845                log.error(
2846                    "Non-absolute path found in %s. If git 2.7.0 was "
2847                    "installed and then downgraded, this was likely caused "
2848                    "by a known issue in git 2.7.0. See "
2849                    "http://permalink.gmane.org/gmane.comp.version-control"
2850                    ".git/283998 for more information.",
2851                    gitdir_file,
2852                )
2853                # Emulate what 'git worktree list' does under-the-hood, and
2854                # that is using the toplevel directory. It will still give
2855                # inaccurate results, but will avoid a traceback.
2856                wt_loc = toplevel
2857
2858            if wt_loc.endswith("/.git"):
2859                wt_loc = wt_loc[:-5]
2860
2861            worktree_is_stale = not os.path.isdir(wt_loc)
2862
2863            if not _desired(worktree_is_stale, all_, stale):
2864                continue
2865
2866            if wt_loc in ret:
2867                _duplicate_worktree_path(wt_loc)
2868
2869            if head_ref.startswith("ref: "):
2870                head_ref = head_ref.split(None, 1)[-1]
2871                wt_branch = head_ref.replace("refs/heads/", "", 1)
2872                wt_head = rev_parse(
2873                    cwd,
2874                    rev=head_ref,
2875                    user=user,
2876                    password=password,
2877                    output_encoding=output_encoding,
2878                )
2879                wt_detached = False
2880            else:
2881                wt_branch = None
2882                wt_head = head_ref
2883                wt_detached = True
2884
2885            wt_ptr = ret.setdefault(wt_loc, {})
2886            wt_ptr["stale"] = worktree_is_stale
2887            wt_ptr["branch"] = wt_branch
2888            wt_ptr["HEAD"] = wt_head
2889            wt_ptr["detached"] = wt_detached
2890
2891            # Check to see if HEAD points at a tag
2892            if wt_detached:
2893                tags_found = _git_tag_points_at(
2894                    cwd,
2895                    wt_head,
2896                    user=user,
2897                    password=password,
2898                    output_encoding=output_encoding,
2899                )
2900                if tags_found:
2901                    wt_ptr["tags"] = tags_found
2902
2903    return ret
2904
2905
2906def ls_remote(
2907    cwd=None,
2908    remote="origin",
2909    ref=None,
2910    opts="",
2911    git_opts="",
2912    user=None,
2913    password=None,
2914    identity=None,
2915    https_user=None,
2916    https_pass=None,
2917    ignore_retcode=False,
2918    output_encoding=None,
2919    saltenv="base",
2920):
2921    """
2922    Interface to `git-ls-remote(1)`_. Returns the upstream hash for a remote
2923    reference.
2924
2925    cwd
2926        The path to the git checkout. Optional (and ignored if present) when
2927        ``remote`` is set to a URL instead of a remote name.
2928
2929    remote : origin
2930        The name of the remote to query. Can be the name of a git remote
2931        (which exists in the git checkout defined by the ``cwd`` parameter),
2932        or the URL of a remote repository.
2933
2934        .. versionchanged:: 2015.8.0
2935            Argument renamed from ``repository`` to ``remote``
2936
2937    ref
2938        The name of the ref to query. Optional, if not specified, all refs are
2939        returned. Can be a branch or tag name, or the full name of the
2940        reference (for example, to get the hash for a Github pull request number
2941        1234, ``ref`` can be set to ``refs/pull/1234/head``
2942
2943        .. versionchanged:: 2015.8.0
2944            Argument renamed from ``branch`` to ``ref``
2945
2946        .. versionchanged:: 2015.8.4
2947            Defaults to returning all refs instead of master.
2948
2949    opts
2950        Any additional options to add to the command line, in a single string
2951
2952        .. versionadded:: 2015.8.0
2953
2954    git_opts
2955        Any additional options to add to git command itself (not the
2956        ``ls-remote`` subcommand), in a single string. This is useful for
2957        passing ``-c`` to run git with temporary changes to the git
2958        configuration.
2959
2960        .. versionadded:: 2017.7.0
2961
2962        .. note::
2963            This is only supported in git 1.7.2 and newer.
2964
2965    user
2966        User under which to run the git command. By default, the command is run
2967        by the user under which the minion is running.
2968
2969    password
2970        Windows only. Required when specifying ``user``. This parameter will be
2971        ignored on non-Windows platforms.
2972
2973      .. versionadded:: 2016.3.4
2974
2975    identity
2976        Path to a private key to use for ssh URLs
2977
2978        .. warning::
2979
2980            Unless Salt is invoked from the minion using ``salt-call``, the
2981            key(s) must be passphraseless. For greater security with
2982            passphraseless private keys, see the `sshd(8)`_ manpage for
2983            information on securing the keypair from the remote side in the
2984            ``authorized_keys`` file.
2985
2986            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
2987
2988        .. versionchanged:: 2015.8.7
2989
2990            Salt will no longer attempt to use passphrase-protected keys unless
2991            invoked from the minion using ``salt-call``, to prevent blocking
2992            waiting for user input.
2993
2994        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
2995
2996        .. versionchanged:: 2016.3.0
2997
2998    https_user
2999        Set HTTP Basic Auth username. Only accepted for HTTPS URLs.
3000
3001        .. versionadded:: 2015.5.0
3002
3003    https_pass
3004        Set HTTP Basic Auth password. Only accepted for HTTPS URLs.
3005
3006        .. versionadded:: 2015.5.0
3007
3008    ignore_retcode : False
3009        If ``True``, do not log an error to the minion log if the git command
3010        returns a nonzero exit status.
3011
3012        .. versionadded:: 2015.8.0
3013
3014    saltenv
3015        The default salt environment to pull sls files from
3016
3017        .. versionadded:: 2016.3.1
3018
3019    output_encoding
3020        Use this option to specify which encoding to use to decode the output
3021        from any git commands which are run. This should not be needed in most
3022        cases.
3023
3024        .. note::
3025            This should only be needed if the files in the repository were
3026            created with filenames using an encoding other than UTF-8 to handle
3027            Unicode characters.
3028
3029        .. versionadded:: 2018.3.1
3030
3031    .. _`git-ls-remote(1)`: http://git-scm.com/docs/git-ls-remote
3032
3033    CLI Example:
3034
3035    .. code-block:: bash
3036
3037        salt myminion git.ls_remote /path/to/repo origin master
3038        salt myminion git.ls_remote remote=https://mydomain.tld/repo.git ref=mytag opts='--tags'
3039    """
3040    if cwd is not None:
3041        cwd = _expand_path(cwd, user)
3042    try:
3043        remote = salt.utils.url.add_http_basic_auth(
3044            remote, https_user, https_pass, https_only=True
3045        )
3046    except ValueError as exc:
3047        raise SaltInvocationError(exc.__str__())
3048    command = ["git"] + _format_git_opts(git_opts)
3049    command.append("ls-remote")
3050    command.extend(_format_opts(opts))
3051    command.append(remote)
3052    if ref:
3053        command.append(ref)
3054    output = _git_run(
3055        command,
3056        cwd=cwd,
3057        user=user,
3058        password=password,
3059        identity=identity,
3060        ignore_retcode=ignore_retcode,
3061        saltenv=saltenv,
3062        output_encoding=output_encoding,
3063    )["stdout"]
3064    ret = {}
3065    for line in output.splitlines():
3066        try:
3067            ref_sha1, ref_name = line.split(None, 1)
3068        except IndexError:
3069            continue
3070        ret[ref_name] = ref_sha1
3071    return ret
3072
3073
3074def merge(
3075    cwd,
3076    rev=None,
3077    opts="",
3078    git_opts="",
3079    user=None,
3080    password=None,
3081    identity=None,
3082    ignore_retcode=False,
3083    output_encoding=None,
3084    **kwargs
3085):
3086    """
3087    Interface to `git-merge(1)`_
3088
3089    cwd
3090        The path to the git checkout
3091
3092    rev
3093        Revision to merge into the current branch. If not specified, the remote
3094        tracking branch will be merged.
3095
3096        .. versionadded:: 2015.8.0
3097
3098    opts
3099        Any additional options to add to the command line, in a single string
3100
3101        .. note::
3102            On the Salt CLI, if the opts are preceded with a dash, it is
3103            necessary to precede them with ``opts=`` (as in the CLI examples
3104            below) to avoid causing errors with Salt's own argument parsing.
3105
3106    git_opts
3107        Any additional options to add to git command itself (not the ``merge``
3108        subcommand), in a single string. This is useful for passing ``-c`` to
3109        run git with temporary changes to the git configuration.
3110
3111        .. versionadded:: 2017.7.0
3112
3113        .. note::
3114            This is only supported in git 1.7.2 and newer.
3115
3116    user
3117        User under which to run the git command. By default, the command is run
3118        by the user under which the minion is running.
3119
3120    password
3121        Windows only. Required when specifying ``user``. This parameter will be
3122        ignored on non-Windows platforms.
3123
3124      .. versionadded:: 2016.3.4
3125
3126    identity
3127        Path to a private key to use for ssh URLs. Salt will not attempt to use
3128        passphrase-protected keys unless invoked from the minion using
3129        ``salt-call``, to prevent blocking waiting for user input. Key can also
3130        be specified as a SaltStack file server URL, eg.
3131        ``salt://location/identity_file``.
3132
3133        .. note::
3134            For greater security with passphraseless private keys, see the
3135            `sshd(8)`_ manpage for information on securing the keypair from the
3136            remote side in the ``authorized_keys`` file.
3137
3138            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
3139
3140        .. versionadded:: 2018.3.5,2019.2.1,3000
3141
3142    ignore_retcode : False
3143        If ``True``, do not log an error to the minion log if the git command
3144        returns a nonzero exit status.
3145
3146        .. versionadded:: 2015.8.0
3147
3148    output_encoding
3149        Use this option to specify which encoding to use to decode the output
3150        from any git commands which are run. This should not be needed in most
3151        cases.
3152
3153        .. note::
3154            This should only be needed if the files in the repository were
3155            created with filenames using an encoding other than UTF-8 to handle
3156            Unicode characters.
3157
3158        .. versionadded:: 2018.3.1
3159
3160    .. _`git-merge(1)`: http://git-scm.com/docs/git-merge
3161
3162    CLI Example:
3163
3164    .. code-block:: bash
3165
3166        # Fetch first...
3167        salt myminion git.fetch /path/to/repo
3168        # ... then merge the remote tracking branch
3169        salt myminion git.merge /path/to/repo
3170        # .. or merge another rev
3171        salt myminion git.merge /path/to/repo rev=upstream/foo
3172    """
3173    kwargs = salt.utils.args.clean_kwargs(**kwargs)
3174    if kwargs:
3175        salt.utils.args.invalid_kwargs(kwargs)
3176
3177    cwd = _expand_path(cwd, user)
3178    command = ["git"] + _format_git_opts(git_opts)
3179    command.append("merge")
3180    command.extend(_format_opts(opts))
3181    if rev:
3182        command.append(rev)
3183
3184    return _git_run(
3185        command,
3186        cwd=cwd,
3187        user=user,
3188        password=password,
3189        identity=identity,
3190        ignore_retcode=ignore_retcode,
3191        output_encoding=output_encoding,
3192    )["stdout"]
3193
3194
3195def merge_base(
3196    cwd,
3197    refs=None,
3198    octopus=False,
3199    is_ancestor=False,
3200    independent=False,
3201    fork_point=None,
3202    opts="",
3203    git_opts="",
3204    user=None,
3205    password=None,
3206    ignore_retcode=False,
3207    output_encoding=None,
3208    **kwargs
3209):
3210    """
3211    .. versionadded:: 2015.8.0
3212
3213    Interface to `git-merge-base(1)`_.
3214
3215    cwd
3216        The path to the git checkout
3217
3218    refs
3219        Any refs/commits to check for a merge base. Can be passed as a
3220        comma-separated list or a Python list.
3221
3222    all : False
3223        Return a list of all matching merge bases. Not compatible with any of
3224        the below options except for ``octopus``.
3225
3226    octopus : False
3227        If ``True``, then this function will determine the best common
3228        ancestors of all specified commits, in preparation for an n-way merge.
3229        See here_ for a description of how these bases are determined.
3230
3231        Set ``all`` to ``True`` with this option to return all computed merge
3232        bases, otherwise only the "best" will be returned.
3233
3234    is_ancestor : False
3235        If ``True``, then instead of returning the merge base, return a
3236        boolean telling whether or not the first commit is an ancestor of the
3237        second commit.
3238
3239        .. note::
3240            This option requires two commits to be passed.
3241
3242        .. versionchanged:: 2015.8.2
3243            Works properly in git versions older than 1.8.0, where the
3244            ``--is-ancestor`` CLI option is not present.
3245
3246    independent : False
3247        If ``True``, this function will return the IDs of the refs/commits
3248        passed which cannot be reached by another commit.
3249
3250    fork_point
3251        If passed, then this function will return the commit where the
3252        commit diverged from the ref specified by ``fork_point``. If no fork
3253        point is found, ``None`` is returned.
3254
3255        .. note::
3256            At most one commit is permitted to be passed if a ``fork_point`` is
3257            specified. If no commits are passed, then ``HEAD`` is assumed.
3258
3259    opts
3260        Any additional options to add to the command line, in a single string
3261
3262        .. note::
3263            On the Salt CLI, if the opts are preceded with a dash, it is
3264            necessary to precede them with ``opts=`` (as in the CLI examples
3265            below) to avoid causing errors with Salt's own argument parsing.
3266
3267            This option should not be necessary unless new CLI arguments are
3268            added to `git-merge-base(1)`_ and are not yet supported in Salt.
3269
3270    git_opts
3271        Any additional options to add to git command itself (not the
3272        ``merge-base`` subcommand), in a single string. This is useful for
3273        passing ``-c`` to run git with temporary changes to the git
3274        configuration.
3275
3276        .. versionadded:: 2017.7.0
3277
3278        .. note::
3279            This is only supported in git 1.7.2 and newer.
3280
3281    user
3282        User under which to run the git command. By default, the command is run
3283        by the user under which the minion is running.
3284
3285    password
3286        Windows only. Required when specifying ``user``. This parameter will be
3287        ignored on non-Windows platforms.
3288
3289      .. versionadded:: 2016.3.4
3290
3291    ignore_retcode : False
3292        if ``True``, do not log an error to the minion log if the git command
3293        returns a nonzero exit status.
3294
3295    output_encoding
3296        Use this option to specify which encoding to use to decode the output
3297        from any git commands which are run. This should not be needed in most
3298        cases.
3299
3300        .. note::
3301            This should only be needed if the files in the repository were
3302            created with filenames using an encoding other than UTF-8 to handle
3303            Unicode characters.
3304
3305        .. versionadded:: 2018.3.1
3306
3307    .. _`git-merge-base(1)`: http://git-scm.com/docs/git-merge-base
3308    .. _here: http://git-scm.com/docs/git-merge-base#_discussion
3309
3310    CLI Examples:
3311
3312    .. code-block:: bash
3313
3314        salt myminion git.merge_base /path/to/repo HEAD upstream/mybranch
3315        salt myminion git.merge_base /path/to/repo 8f2e542,4ad8cab,cdc9886 octopus=True
3316        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab,cdc9886 independent=True
3317        salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab is_ancestor=True
3318        salt myminion git.merge_base /path/to/repo fork_point=upstream/master
3319        salt myminion git.merge_base /path/to/repo refs=mybranch fork_point=upstream/master
3320    """
3321    cwd = _expand_path(cwd, user)
3322    kwargs = salt.utils.args.clean_kwargs(**kwargs)
3323    all_ = kwargs.pop("all", False)
3324    if kwargs:
3325        salt.utils.args.invalid_kwargs(kwargs)
3326
3327    if all_ and (independent or is_ancestor or fork_point):
3328        raise SaltInvocationError(
3329            "The 'all' argument is not compatible with 'independent', "
3330            "'is_ancestor', or 'fork_point'"
3331        )
3332
3333    if refs is None:
3334        refs = []
3335    elif not isinstance(refs, (list, tuple)):
3336        refs = [x.strip() for x in str(refs).split(",")]
3337    mutually_exclusive_count = len(
3338        [x for x in (octopus, independent, is_ancestor, fork_point) if x]
3339    )
3340    if mutually_exclusive_count > 1:
3341        raise SaltInvocationError(
3342            "Only one of 'octopus', 'independent', 'is_ancestor', and "
3343            "'fork_point' is permitted"
3344        )
3345    elif is_ancestor:
3346        if len(refs) != 2:
3347            raise SaltInvocationError(
3348                "Two refs/commits are required if 'is_ancestor' is True"
3349            )
3350    elif fork_point:
3351        if len(refs) > 1:
3352            raise SaltInvocationError(
3353                "At most one ref/commit can be passed if 'fork_point' is specified"
3354            )
3355        elif not refs:
3356            refs = ["HEAD"]
3357
3358    if is_ancestor:
3359        if _LooseVersion(version(versioninfo=False)) < _LooseVersion("1.8.0"):
3360            # Pre 1.8.0 git doesn't have --is-ancestor, so the logic here is a
3361            # little different. First we need to resolve the first ref to a
3362            # full SHA1, and then if running git merge-base on both commits
3363            # returns an identical commit to the resolved first ref, we know
3364            # that the first ref is an ancestor of the second ref.
3365            first_commit = rev_parse(
3366                cwd,
3367                rev=refs[0],
3368                opts=["--verify"],
3369                user=user,
3370                password=password,
3371                ignore_retcode=ignore_retcode,
3372                output_encoding=output_encoding,
3373            )
3374            return (
3375                merge_base(
3376                    cwd,
3377                    refs=refs,
3378                    is_ancestor=False,
3379                    user=user,
3380                    password=password,
3381                    ignore_retcode=ignore_retcode,
3382                    output_encoding=output_encoding,
3383                )
3384                == first_commit
3385            )
3386
3387    command = ["git"] + _format_git_opts(git_opts)
3388    command.append("merge-base")
3389    command.extend(_format_opts(opts))
3390    if all_:
3391        command.append("--all")
3392    if octopus:
3393        command.append("--octopus")
3394    elif is_ancestor:
3395        command.append("--is-ancestor")
3396    elif independent:
3397        command.append("--independent")
3398    elif fork_point:
3399        command.extend(["--fork-point", fork_point])
3400    command.extend(refs)
3401    result = _git_run(
3402        command,
3403        cwd=cwd,
3404        user=user,
3405        password=password,
3406        ignore_retcode=ignore_retcode,
3407        failhard=False if is_ancestor else True,
3408        output_encoding=output_encoding,
3409    )
3410    if is_ancestor:
3411        return result["retcode"] == 0
3412    all_bases = result["stdout"].splitlines()
3413    if all_:
3414        return all_bases
3415    return all_bases[0]
3416
3417
3418def merge_tree(
3419    cwd,
3420    ref1,
3421    ref2,
3422    base=None,
3423    user=None,
3424    password=None,
3425    ignore_retcode=False,
3426    output_encoding=None,
3427):
3428    """
3429    .. versionadded:: 2015.8.0
3430
3431    Interface to `git-merge-tree(1)`_, shows the merge results and conflicts
3432    from a 3-way merge without touching the index.
3433
3434    cwd
3435        The path to the git checkout
3436
3437    ref1
3438        First ref/commit to compare
3439
3440    ref2
3441        Second ref/commit to compare
3442
3443    base
3444        The base tree to use for the 3-way-merge. If not provided, then
3445        :py:func:`git.merge_base <salt.modules.git.merge_base>` will be invoked
3446        on ``ref1`` and ``ref2`` to determine the merge base to use.
3447
3448    user
3449        User under which to run the git command. By default, the command is run
3450        by the user under which the minion is running.
3451
3452    password
3453        Windows only. Required when specifying ``user``. This parameter will be
3454        ignored on non-Windows platforms.
3455
3456      .. versionadded:: 2016.3.4
3457
3458    ignore_retcode : False
3459        if ``True``, do not log an error to the minion log if the git command
3460        returns a nonzero exit status.
3461
3462    output_encoding
3463        Use this option to specify which encoding to use to decode the output
3464        from any git commands which are run. This should not be needed in most
3465        cases.
3466
3467        .. note::
3468            This should only be needed if the files in the repository were
3469            created with filenames using an encoding other than UTF-8 to handle
3470            Unicode characters.
3471
3472        .. versionadded:: 2018.3.1
3473
3474    .. _`git-merge-tree(1)`: http://git-scm.com/docs/git-merge-tree
3475
3476    CLI Examples:
3477
3478    .. code-block:: bash
3479
3480        salt myminion git.merge_tree /path/to/repo HEAD upstream/dev
3481        salt myminion git.merge_tree /path/to/repo HEAD upstream/dev base=aaf3c3d
3482    """
3483    cwd = _expand_path(cwd, user)
3484    command = ["git", "merge-tree"]
3485    if base is None:
3486        try:
3487            base = merge_base(cwd, refs=[ref1, ref2], output_encoding=output_encoding)
3488        except (SaltInvocationError, CommandExecutionError):
3489            raise CommandExecutionError(
3490                "Unable to determine merge base for {} and {}".format(ref1, ref2)
3491            )
3492    command.extend([base, ref1, ref2])
3493    return _git_run(
3494        command,
3495        cwd=cwd,
3496        user=user,
3497        password=password,
3498        ignore_retcode=ignore_retcode,
3499        output_encoding=output_encoding,
3500    )["stdout"]
3501
3502
3503def pull(
3504    cwd,
3505    opts="",
3506    git_opts="",
3507    user=None,
3508    password=None,
3509    identity=None,
3510    ignore_retcode=False,
3511    saltenv="base",
3512    output_encoding=None,
3513):
3514    """
3515    Interface to `git-pull(1)`_
3516
3517    cwd
3518        The path to the git checkout
3519
3520    opts
3521        Any additional options to add to the command line, in a single string
3522
3523        .. note::
3524            On the Salt CLI, if the opts are preceded with a dash, it is
3525            necessary to precede them with ``opts=`` (as in the CLI examples
3526            below) to avoid causing errors with Salt's own argument parsing.
3527
3528    git_opts
3529        Any additional options to add to git command itself (not the ``pull``
3530        subcommand), in a single string. This is useful for passing ``-c`` to
3531        run git with temporary changes to the git configuration.
3532
3533        .. versionadded:: 2017.7.0
3534
3535        .. note::
3536            This is only supported in git 1.7.2 and newer.
3537
3538    user
3539        User under which to run the git command. By default, the command is run
3540        by the user under which the minion is running.
3541
3542    password
3543        Windows only. Required when specifying ``user``. This parameter will be
3544        ignored on non-Windows platforms.
3545
3546      .. versionadded:: 2016.3.4
3547
3548    identity
3549        Path to a private key to use for ssh URLs
3550
3551        .. warning::
3552
3553            Unless Salt is invoked from the minion using ``salt-call``, the
3554            key(s) must be passphraseless. For greater security with
3555            passphraseless private keys, see the `sshd(8)`_ manpage for
3556            information on securing the keypair from the remote side in the
3557            ``authorized_keys`` file.
3558
3559            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
3560
3561        .. versionchanged:: 2015.8.7
3562
3563            Salt will no longer attempt to use passphrase-protected keys unless
3564            invoked from the minion using ``salt-call``, to prevent blocking
3565            waiting for user input.
3566
3567        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
3568
3569        .. versionchanged:: 2016.3.0
3570
3571    ignore_retcode : False
3572        If ``True``, do not log an error to the minion log if the git command
3573        returns a nonzero exit status.
3574
3575        .. versionadded:: 2015.8.0
3576
3577    saltenv
3578        The default salt environment to pull sls files from
3579
3580        .. versionadded:: 2016.3.1
3581
3582    output_encoding
3583        Use this option to specify which encoding to use to decode the output
3584        from any git commands which are run. This should not be needed in most
3585        cases.
3586
3587        .. note::
3588            This should only be needed if the files in the repository were
3589            created with filenames using an encoding other than UTF-8 to handle
3590            Unicode characters.
3591
3592        .. versionadded:: 2018.3.1
3593
3594    .. _`git-pull(1)`: http://git-scm.com/docs/git-pull
3595
3596    CLI Example:
3597
3598    .. code-block:: bash
3599
3600        salt myminion git.pull /path/to/repo opts='--rebase origin master'
3601    """
3602    cwd = _expand_path(cwd, user)
3603    command = ["git"] + _format_git_opts(git_opts)
3604    command.append("pull")
3605    command.extend(_format_opts(opts))
3606    return _git_run(
3607        command,
3608        cwd=cwd,
3609        user=user,
3610        password=password,
3611        identity=identity,
3612        ignore_retcode=ignore_retcode,
3613        saltenv=saltenv,
3614        output_encoding=output_encoding,
3615    )["stdout"]
3616
3617
3618def push(
3619    cwd,
3620    remote=None,
3621    ref=None,
3622    opts="",
3623    git_opts="",
3624    user=None,
3625    password=None,
3626    identity=None,
3627    ignore_retcode=False,
3628    saltenv="base",
3629    output_encoding=None,
3630    **kwargs
3631):
3632    """
3633    Interface to `git-push(1)`_
3634
3635    cwd
3636        The path to the git checkout
3637
3638    remote
3639        Name of the remote to which the ref should being pushed
3640
3641        .. versionadded:: 2015.8.0
3642
3643    ref : master
3644        Name of the ref to push
3645
3646        .. note::
3647            Being a refspec_, this argument can include a colon to define local
3648            and remote ref names.
3649
3650    opts
3651        Any additional options to add to the command line, in a single string
3652
3653        .. note::
3654            On the Salt CLI, if the opts are preceded with a dash, it is
3655            necessary to precede them with ``opts=`` (as in the CLI examples
3656            below) to avoid causing errors with Salt's own argument parsing.
3657
3658    git_opts
3659        Any additional options to add to git command itself (not the ``push``
3660        subcommand), in a single string. This is useful for passing ``-c`` to
3661        run git with temporary changes to the git configuration.
3662
3663        .. versionadded:: 2017.7.0
3664
3665        .. note::
3666            This is only supported in git 1.7.2 and newer.
3667
3668    user
3669        User under which to run the git command. By default, the command is run
3670        by the user under which the minion is running.
3671
3672    password
3673        Windows only. Required when specifying ``user``. This parameter will be
3674        ignored on non-Windows platforms.
3675
3676      .. versionadded:: 2016.3.4
3677
3678    identity
3679        Path to a private key to use for ssh URLs
3680
3681        .. warning::
3682
3683            Unless Salt is invoked from the minion using ``salt-call``, the
3684            key(s) must be passphraseless. For greater security with
3685            passphraseless private keys, see the `sshd(8)`_ manpage for
3686            information on securing the keypair from the remote side in the
3687            ``authorized_keys`` file.
3688
3689            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
3690
3691        .. versionchanged:: 2015.8.7
3692
3693            Salt will no longer attempt to use passphrase-protected keys unless
3694            invoked from the minion using ``salt-call``, to prevent blocking
3695            waiting for user input.
3696
3697        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
3698
3699        .. versionchanged:: 2016.3.0
3700
3701    ignore_retcode : False
3702        If ``True``, do not log an error to the minion log if the git command
3703        returns a nonzero exit status.
3704
3705        .. versionadded:: 2015.8.0
3706
3707    saltenv
3708        The default salt environment to pull sls files from
3709
3710        .. versionadded:: 2016.3.1
3711
3712    output_encoding
3713        Use this option to specify which encoding to use to decode the output
3714        from any git commands which are run. This should not be needed in most
3715        cases.
3716
3717        .. note::
3718            This should only be needed if the files in the repository were
3719            created with filenames using an encoding other than UTF-8 to handle
3720            Unicode characters.
3721
3722        .. versionadded:: 2018.3.1
3723
3724    .. _`git-push(1)`: http://git-scm.com/docs/git-push
3725    .. _refspec: http://git-scm.com/book/en/v2/Git-Internals-The-Refspec
3726
3727    CLI Example:
3728
3729    .. code-block:: bash
3730
3731        # Push master as origin/master
3732        salt myminion git.push /path/to/repo origin master
3733        # Push issue21 as upstream/develop
3734        salt myminion git.push /path/to/repo upstream issue21:develop
3735        # Delete remote branch 'upstream/temp'
3736        salt myminion git.push /path/to/repo upstream :temp
3737    """
3738    kwargs = salt.utils.args.clean_kwargs(**kwargs)
3739    if kwargs:
3740        salt.utils.args.invalid_kwargs(kwargs)
3741
3742    cwd = _expand_path(cwd, user)
3743    command = ["git"] + _format_git_opts(git_opts)
3744    command.append("push")
3745    command.extend(_format_opts(opts))
3746    command.extend([remote, ref])
3747    return _git_run(
3748        command,
3749        cwd=cwd,
3750        user=user,
3751        password=password,
3752        identity=identity,
3753        ignore_retcode=ignore_retcode,
3754        saltenv=saltenv,
3755        output_encoding=output_encoding,
3756    )["stdout"]
3757
3758
3759def rebase(
3760    cwd,
3761    rev="master",
3762    opts="",
3763    git_opts="",
3764    user=None,
3765    password=None,
3766    ignore_retcode=False,
3767    output_encoding=None,
3768):
3769    """
3770    Interface to `git-rebase(1)`_
3771
3772    cwd
3773        The path to the git checkout
3774
3775    rev : master
3776        The revision to rebase onto the current branch
3777
3778    opts
3779        Any additional options to add to the command line, in a single string
3780
3781        .. note::
3782            On the Salt CLI, if the opts are preceded with a dash, it is
3783            necessary to precede them with ``opts=`` (as in the CLI examples
3784            below) to avoid causing errors with Salt's own argument parsing.
3785
3786    git_opts
3787        Any additional options to add to git command itself (not the ``rebase``
3788        subcommand), in a single string. This is useful for passing ``-c`` to
3789        run git with temporary changes to the git configuration.
3790
3791        .. versionadded:: 2017.7.0
3792
3793        .. note::
3794            This is only supported in git 1.7.2 and newer.
3795
3796    user
3797        User under which to run the git command. By default, the command is run
3798        by the user under which the minion is running.
3799
3800    password
3801        Windows only. Required when specifying ``user``. This parameter will be
3802        ignored on non-Windows platforms.
3803
3804      .. versionadded:: 2016.3.4
3805
3806    ignore_retcode : False
3807        If ``True``, do not log an error to the minion log if the git command
3808        returns a nonzero exit status.
3809
3810        .. versionadded:: 2015.8.0
3811
3812    output_encoding
3813        Use this option to specify which encoding to use to decode the output
3814        from any git commands which are run. This should not be needed in most
3815        cases.
3816
3817        .. note::
3818            This should only be needed if the files in the repository were
3819            created with filenames using an encoding other than UTF-8 to handle
3820            Unicode characters.
3821
3822        .. versionadded:: 2018.3.1
3823
3824    .. _`git-rebase(1)`: http://git-scm.com/docs/git-rebase
3825
3826    CLI Example:
3827
3828    .. code-block:: bash
3829
3830        salt myminion git.rebase /path/to/repo master
3831        salt myminion git.rebase /path/to/repo 'origin master'
3832        salt myminion git.rebase /path/to/repo origin/master opts='--onto newbranch'
3833    """
3834    cwd = _expand_path(cwd, user)
3835    opts = _format_opts(opts)
3836    if any(x for x in opts if x in ("-i", "--interactive")):
3837        raise SaltInvocationError("Interactive rebases are not supported")
3838    command = ["git"] + _format_git_opts(git_opts)
3839    command.append("rebase")
3840    command.extend(opts)
3841    if not isinstance(rev, str):
3842        rev = str(rev)
3843    command.extend(salt.utils.args.shlex_split(rev))
3844    return _git_run(
3845        command,
3846        cwd=cwd,
3847        user=user,
3848        password=password,
3849        ignore_retcode=ignore_retcode,
3850        output_encoding=output_encoding,
3851    )["stdout"]
3852
3853
3854def remote_get(
3855    cwd,
3856    remote="origin",
3857    user=None,
3858    password=None,
3859    redact_auth=True,
3860    ignore_retcode=False,
3861    output_encoding=None,
3862):
3863    """
3864    Get the fetch and push URL for a specific remote
3865
3866    cwd
3867        The path to the git checkout
3868
3869    remote : origin
3870        Name of the remote to query
3871
3872    user
3873        User under which to run the git command. By default, the command is run
3874        by the user under which the minion is running.
3875
3876    password
3877        Windows only. Required when specifying ``user``. This parameter will be
3878        ignored on non-Windows platforms.
3879
3880      .. versionadded:: 2016.3.4
3881
3882    redact_auth : True
3883        Set to ``False`` to include the username/password if the remote uses
3884        HTTPS Basic Auth. Otherwise, this information will be redacted.
3885
3886        .. warning::
3887            Setting this to ``False`` will not only reveal any HTTPS Basic Auth
3888            that is configured, but the return data will also be written to the
3889            job cache. When possible, it is recommended to use SSH for
3890            authentication.
3891
3892        .. versionadded:: 2015.5.6
3893
3894    ignore_retcode : False
3895        If ``True``, do not log an error to the minion log if the git command
3896        returns a nonzero exit status.
3897
3898        .. versionadded:: 2015.8.0
3899
3900    output_encoding
3901        Use this option to specify which encoding to use to decode the output
3902        from any git commands which are run. This should not be needed in most
3903        cases.
3904
3905        .. note::
3906            This should only be needed if the files in the repository were
3907            created with filenames using an encoding other than UTF-8 to handle
3908            Unicode characters.
3909
3910        .. versionadded:: 2018.3.1
3911
3912    CLI Examples:
3913
3914    .. code-block:: bash
3915
3916        salt myminion git.remote_get /path/to/repo
3917        salt myminion git.remote_get /path/to/repo upstream
3918    """
3919    cwd = _expand_path(cwd, user)
3920    all_remotes = remotes(
3921        cwd,
3922        user=user,
3923        password=password,
3924        redact_auth=redact_auth,
3925        ignore_retcode=ignore_retcode,
3926        output_encoding=output_encoding,
3927    )
3928    if remote not in all_remotes:
3929        raise CommandExecutionError(
3930            "Remote '{}' not present in git checkout located at {}".format(remote, cwd)
3931        )
3932    return all_remotes[remote]
3933
3934
3935def remote_refs(
3936    url,
3937    heads=False,
3938    tags=False,
3939    user=None,
3940    password=None,
3941    identity=None,
3942    https_user=None,
3943    https_pass=None,
3944    ignore_retcode=False,
3945    output_encoding=None,
3946    saltenv="base",
3947    **kwargs
3948):
3949    """
3950    .. versionadded:: 2015.8.0
3951
3952    Return the remote refs for the specified URL by running ``git ls-remote``.
3953
3954    url
3955        URL of the remote repository
3956
3957    filter
3958        Optionally provide a ref name to ``git ls-remote``. This can be useful
3959        to make this function run faster on repositories with many
3960        branches/tags.
3961
3962        .. versionadded:: 2019.2.0
3963
3964    heads : False
3965        Restrict output to heads. Can be combined with ``tags``.
3966
3967    tags : False
3968        Restrict output to tags. Can be combined with ``heads``.
3969
3970    user
3971        User under which to run the git command. By default, the command is run
3972        by the user under which the minion is running.
3973
3974    password
3975        Windows only. Required when specifying ``user``. This parameter will be
3976        ignored on non-Windows platforms.
3977
3978      .. versionadded:: 2016.3.4
3979
3980    identity
3981        Path to a private key to use for ssh URLs
3982
3983        .. warning::
3984
3985            Unless Salt is invoked from the minion using ``salt-call``, the
3986            key(s) must be passphraseless. For greater security with
3987            passphraseless private keys, see the `sshd(8)`_ manpage for
3988            information on securing the keypair from the remote side in the
3989            ``authorized_keys`` file.
3990
3991            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
3992
3993        .. versionchanged:: 2015.8.7
3994
3995            Salt will no longer attempt to use passphrase-protected keys unless
3996            invoked from the minion using ``salt-call``, to prevent blocking
3997            waiting for user input.
3998
3999        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
4000
4001        .. versionchanged:: 2016.3.0
4002
4003    https_user
4004        Set HTTP Basic Auth username. Only accepted for HTTPS URLs.
4005
4006    https_pass
4007        Set HTTP Basic Auth password. Only accepted for HTTPS URLs.
4008
4009    ignore_retcode : False
4010        If ``True``, do not log an error to the minion log if the git command
4011        returns a nonzero exit status.
4012
4013    saltenv
4014        The default salt environment to pull sls files from
4015
4016        .. versionadded:: 2016.3.1
4017
4018    output_encoding
4019        Use this option to specify which encoding to use to decode the output
4020        from any git commands which are run. This should not be needed in most
4021        cases.
4022
4023        .. note::
4024            This should only be needed if the files in the repository were
4025            created with filenames using an encoding other than UTF-8 to handle
4026            Unicode characters.
4027
4028        .. versionadded:: 2018.3.1
4029
4030    CLI Example:
4031
4032    .. code-block:: bash
4033
4034        salt myminion git.remote_refs https://github.com/saltstack/salt.git
4035        salt myminion git.remote_refs https://github.com/saltstack/salt.git filter=develop
4036    """
4037    kwargs = salt.utils.args.clean_kwargs(**kwargs)
4038    filter_ = kwargs.pop("filter", None)
4039    if kwargs:
4040        salt.utils.args.invalid_kwargs(kwargs)
4041
4042    command = ["git", "ls-remote"]
4043    if heads:
4044        command.append("--heads")
4045    if tags:
4046        command.append("--tags")
4047    try:
4048        command.append(
4049            salt.utils.url.add_http_basic_auth(
4050                url, https_user, https_pass, https_only=True
4051            )
4052        )
4053    except ValueError as exc:
4054        raise SaltInvocationError(exc.__str__())
4055    if filter_:
4056        command.append(filter_)
4057    output = _git_run(
4058        command,
4059        user=user,
4060        password=password,
4061        identity=identity,
4062        ignore_retcode=ignore_retcode,
4063        saltenv=saltenv,
4064        output_encoding=output_encoding,
4065    )["stdout"]
4066    ret = {}
4067    for line in salt.utils.itertools.split(output, "\n"):
4068        try:
4069            sha1_hash, ref_name = line.split(None, 1)
4070        except ValueError:
4071            continue
4072        ret[ref_name] = sha1_hash
4073    return ret
4074
4075
4076def remote_set(
4077    cwd,
4078    url,
4079    remote="origin",
4080    user=None,
4081    password=None,
4082    https_user=None,
4083    https_pass=None,
4084    push_url=None,
4085    push_https_user=None,
4086    push_https_pass=None,
4087    ignore_retcode=False,
4088    output_encoding=None,
4089):
4090    """
4091    cwd
4092        The path to the git checkout
4093
4094    url
4095        Remote URL to set
4096
4097    remote : origin
4098        Name of the remote to set
4099
4100    push_url
4101        If unset, the push URL will be identical to the fetch URL.
4102
4103        .. versionadded:: 2015.8.0
4104
4105    user
4106        User under which to run the git command. By default, the command is run
4107        by the user under which the minion is running.
4108
4109    password
4110        Windows only. Required when specifying ``user``. This parameter will be
4111        ignored on non-Windows platforms.
4112
4113      .. versionadded:: 2016.3.4
4114
4115    https_user
4116        Set HTTP Basic Auth username. Only accepted for HTTPS URLs.
4117
4118        .. versionadded:: 2015.5.0
4119
4120    https_pass
4121        Set HTTP Basic Auth password. Only accepted for HTTPS URLs.
4122
4123        .. versionadded:: 2015.5.0
4124
4125    push_https_user
4126        Set HTTP Basic Auth user for ``push_url``. Ignored if ``push_url`` is
4127        unset. Only accepted for HTTPS URLs.
4128
4129        .. versionadded:: 2015.8.0
4130
4131    push_https_pass
4132        Set HTTP Basic Auth password for ``push_url``. Ignored if ``push_url``
4133        is unset. Only accepted for HTTPS URLs.
4134
4135        .. versionadded:: 2015.8.0
4136
4137    ignore_retcode : False
4138        If ``True``, do not log an error to the minion log if the git command
4139        returns a nonzero exit status.
4140
4141        .. versionadded:: 2015.8.0
4142
4143    output_encoding
4144        Use this option to specify which encoding to use to decode the output
4145        from any git commands which are run. This should not be needed in most
4146        cases.
4147
4148        .. note::
4149            This should only be needed if the files in the repository were
4150            created with filenames using an encoding other than UTF-8 to handle
4151            Unicode characters.
4152
4153        .. versionadded:: 2018.3.1
4154
4155    CLI Examples:
4156
4157    .. code-block:: bash
4158
4159        salt myminion git.remote_set /path/to/repo git@github.com:user/repo.git
4160        salt myminion git.remote_set /path/to/repo git@github.com:user/repo.git remote=upstream
4161        salt myminion git.remote_set /path/to/repo https://github.com/user/repo.git remote=upstream push_url=git@github.com:user/repo.git
4162    """
4163    # Check if remote exists
4164    if remote in remotes(
4165        cwd, user=user, password=password, output_encoding=output_encoding
4166    ):
4167        log.debug(
4168            "Remote '%s' already exists in git checkout located at %s, "
4169            "removing so it can be re-added",
4170            remote,
4171            cwd,
4172        )
4173        command = ["git", "remote", "rm", remote]
4174        _git_run(
4175            command,
4176            cwd=cwd,
4177            user=user,
4178            password=password,
4179            ignore_retcode=ignore_retcode,
4180            output_encoding=output_encoding,
4181        )
4182    # Add remote
4183    try:
4184        url = salt.utils.url.add_http_basic_auth(
4185            url, https_user, https_pass, https_only=True
4186        )
4187    except ValueError as exc:
4188        raise SaltInvocationError(exc.__str__())
4189    command = ["git", "remote", "add", remote, url]
4190    _git_run(
4191        command,
4192        cwd=cwd,
4193        user=user,
4194        password=password,
4195        ignore_retcode=ignore_retcode,
4196        output_encoding=output_encoding,
4197    )
4198    if push_url:
4199        if not isinstance(push_url, str):
4200            push_url = str(push_url)
4201        try:
4202            push_url = salt.utils.url.add_http_basic_auth(
4203                push_url, push_https_user, push_https_pass, https_only=True
4204            )
4205        except ValueError as exc:
4206            raise SaltInvocationError(str(exc))
4207        command = ["git", "remote", "set-url", "--push", remote, push_url]
4208        _git_run(
4209            command,
4210            cwd=cwd,
4211            user=user,
4212            password=password,
4213            ignore_retcode=ignore_retcode,
4214            output_encoding=output_encoding,
4215        )
4216    return remote_get(
4217        cwd=cwd,
4218        remote=remote,
4219        user=user,
4220        password=password,
4221        ignore_retcode=ignore_retcode,
4222        output_encoding=output_encoding,
4223    )
4224
4225
4226def remotes(
4227    cwd,
4228    user=None,
4229    password=None,
4230    redact_auth=True,
4231    ignore_retcode=False,
4232    output_encoding=None,
4233):
4234    """
4235    Get fetch and push URLs for each remote in a git checkout
4236
4237    cwd
4238        The path to the git checkout
4239
4240    user
4241        User under which to run the git command. By default, the command is run
4242        by the user under which the minion is running.
4243
4244    password
4245        Windows only. Required when specifying ``user``. This parameter will be
4246        ignored on non-Windows platforms.
4247
4248      .. versionadded:: 2016.3.4
4249
4250    redact_auth : True
4251        Set to ``False`` to include the username/password for authenticated
4252        remotes in the return data. Otherwise, this information will be
4253        redacted.
4254
4255        .. warning::
4256            Setting this to ``False`` will not only reveal any HTTPS Basic Auth
4257            that is configured, but the return data will also be written to the
4258            job cache. When possible, it is recommended to use SSH for
4259            authentication.
4260
4261        .. versionadded:: 2015.5.6
4262
4263    ignore_retcode : False
4264        If ``True``, do not log an error to the minion log if the git command
4265        returns a nonzero exit status.
4266
4267        .. versionadded:: 2015.8.0
4268
4269    output_encoding
4270        Use this option to specify which encoding to use to decode the output
4271        from any git commands which are run. This should not be needed in most
4272        cases.
4273
4274        .. note::
4275            This should only be needed if the files in the repository were
4276            created with filenames using an encoding other than UTF-8 to handle
4277            Unicode characters.
4278
4279        .. versionadded:: 2018.3.1
4280
4281    CLI Example:
4282
4283    .. code-block:: bash
4284
4285        salt myminion git.remotes /path/to/repo
4286    """
4287    cwd = _expand_path(cwd, user)
4288    command = ["git", "remote", "--verbose"]
4289    ret = {}
4290    output = _git_run(
4291        command,
4292        cwd=cwd,
4293        user=user,
4294        password=password,
4295        ignore_retcode=ignore_retcode,
4296        output_encoding=output_encoding,
4297    )["stdout"]
4298    for remote_line in salt.utils.itertools.split(output, "\n"):
4299        try:
4300            remote, remote_info = remote_line.split(None, 1)
4301        except ValueError:
4302            continue
4303        try:
4304            remote_url, action = remote_info.rsplit(None, 1)
4305        except ValueError:
4306            continue
4307        # Remove parenthesis
4308        action = action.lstrip("(").rstrip(")").lower()
4309        if action not in ("fetch", "push"):
4310            log.warning(
4311                "Unknown action '%s' for remote '%s' in git checkout located in %s",
4312                action,
4313                remote,
4314                cwd,
4315            )
4316            continue
4317        if redact_auth:
4318            remote_url = salt.utils.url.redact_http_basic_auth(remote_url)
4319        ret.setdefault(remote, {})[action] = remote_url
4320    return ret
4321
4322
4323def reset(
4324    cwd,
4325    opts="",
4326    git_opts="",
4327    user=None,
4328    password=None,
4329    identity=None,
4330    ignore_retcode=False,
4331    output_encoding=None,
4332):
4333    """
4334    Interface to `git-reset(1)`_, returns the stdout from the git command
4335
4336    cwd
4337        The path to the git checkout
4338
4339    opts
4340        Any additional options to add to the command line, in a single string
4341
4342        .. note::
4343            On the Salt CLI, if the opts are preceded with a dash, it is
4344            necessary to precede them with ``opts=`` (as in the CLI examples
4345            below) to avoid causing errors with Salt's own argument parsing.
4346
4347    git_opts
4348        Any additional options to add to git command itself (not the ``reset``
4349        subcommand), in a single string. This is useful for passing ``-c`` to
4350        run git with temporary changes to the git configuration.
4351
4352        .. versionadded:: 2017.7.0
4353
4354        .. note::
4355            This is only supported in git 1.7.2 and newer.
4356
4357    user
4358        User under which to run the git command. By default, the command is run
4359        by the user under which the minion is running.
4360
4361    password
4362        Windows only. Required when specifying ``user``. This parameter will be
4363        ignored on non-Windows platforms.
4364
4365      .. versionadded:: 2016.3.4
4366
4367    identity
4368        Path to a private key to use for ssh URLs. Salt will not attempt to use
4369        passphrase-protected keys unless invoked from the minion using
4370        ``salt-call``, to prevent blocking waiting for user input. Key can also
4371        be specified as a SaltStack file server URL, eg.
4372        ``salt://location/identity_file``.
4373
4374        .. note::
4375            For greater security with passphraseless private keys, see the
4376            `sshd(8)`_ manpage for information on securing the keypair from the
4377            remote side in the ``authorized_keys`` file.
4378
4379            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
4380
4381        .. versionadded:: 2018.3.5,2019.2.1,3000
4382
4383    ignore_retcode : False
4384        If ``True``, do not log an error to the minion log if the git command
4385        returns a nonzero exit status.
4386
4387        .. versionadded:: 2015.8.0
4388
4389    output_encoding
4390        Use this option to specify which encoding to use to decode the output
4391        from any git commands which are run. This should not be needed in most
4392        cases.
4393
4394        .. note::
4395            This should only be needed if the files in the repository were
4396            created with filenames using an encoding other than UTF-8 to handle
4397            Unicode characters.
4398
4399        .. versionadded:: 2018.3.1
4400
4401    .. _`git-reset(1)`: http://git-scm.com/docs/git-reset
4402
4403    CLI Examples:
4404
4405    .. code-block:: bash
4406
4407        # Soft reset to a specific commit ID
4408        salt myminion git.reset /path/to/repo ac3ee5c
4409        # Hard reset
4410        salt myminion git.reset /path/to/repo opts='--hard origin/master'
4411    """
4412    cwd = _expand_path(cwd, user)
4413    command = ["git"] + _format_git_opts(git_opts)
4414    command.append("reset")
4415    command.extend(_format_opts(opts))
4416    return _git_run(
4417        command,
4418        cwd=cwd,
4419        user=user,
4420        password=password,
4421        identity=identity,
4422        ignore_retcode=ignore_retcode,
4423        output_encoding=output_encoding,
4424    )["stdout"]
4425
4426
4427def rev_parse(
4428    cwd,
4429    rev=None,
4430    opts="",
4431    git_opts="",
4432    user=None,
4433    password=None,
4434    ignore_retcode=False,
4435    output_encoding=None,
4436):
4437    """
4438    .. versionadded:: 2015.8.0
4439
4440    Interface to `git-rev-parse(1)`_
4441
4442    cwd
4443        The path to the git checkout
4444
4445    rev
4446        Revision to parse. See the `SPECIFYING REVISIONS`_ section of the
4447        `git-rev-parse(1)`_ manpage for details on how to format this argument.
4448
4449        This argument is optional when using the options in the `Options for
4450        Files` section of the `git-rev-parse(1)`_ manpage.
4451
4452    opts
4453        Any additional options to add to the command line, in a single string
4454
4455    git_opts
4456        Any additional options to add to git command itself (not the
4457        ``rev-parse`` subcommand), in a single string. This is useful for
4458        passing ``-c`` to run git with temporary changes to the git
4459        configuration.
4460
4461        .. versionadded:: 2017.7.0
4462
4463        .. note::
4464            This is only supported in git 1.7.2 and newer.
4465
4466    user
4467        User under which to run the git command. By default, the command is run
4468        by the user under which the minion is running.
4469
4470    password
4471        Windows only. Required when specifying ``user``. This parameter will be
4472        ignored on non-Windows platforms.
4473
4474      .. versionadded:: 2016.3.4
4475
4476    ignore_retcode : False
4477        If ``True``, do not log an error to the minion log if the git command
4478        returns a nonzero exit status.
4479
4480    output_encoding
4481        Use this option to specify which encoding to use to decode the output
4482        from any git commands which are run. This should not be needed in most
4483        cases.
4484
4485        .. note::
4486            This should only be needed if the files in the repository were
4487            created with filenames using an encoding other than UTF-8 to handle
4488            Unicode characters.
4489
4490        .. versionadded:: 2018.3.1
4491
4492    .. _`git-rev-parse(1)`: http://git-scm.com/docs/git-rev-parse
4493    .. _`SPECIFYING REVISIONS`: http://git-scm.com/docs/git-rev-parse#_specifying_revisions
4494    .. _`Options for Files`: http://git-scm.com/docs/git-rev-parse#_options_for_files
4495
4496    CLI Examples:
4497
4498    .. code-block:: bash
4499
4500        # Get the full SHA1 for HEAD
4501        salt myminion git.rev_parse /path/to/repo HEAD
4502        # Get the short SHA1 for HEAD
4503        salt myminion git.rev_parse /path/to/repo HEAD opts='--short'
4504        # Get the develop branch's upstream tracking branch
4505        salt myminion git.rev_parse /path/to/repo 'develop@{upstream}' opts='--abbrev-ref'
4506        # Get the SHA1 for the commit corresponding to tag v1.2.3
4507        salt myminion git.rev_parse /path/to/repo 'v1.2.3^{commit}'
4508        # Find out whether or not the repo at /path/to/repo is a bare repository
4509        salt myminion git.rev_parse /path/to/repo opts='--is-bare-repository'
4510    """
4511    cwd = _expand_path(cwd, user)
4512    command = ["git"] + _format_git_opts(git_opts)
4513    command.append("rev-parse")
4514    command.extend(_format_opts(opts))
4515    if rev is not None:
4516        command.append(rev)
4517    return _git_run(
4518        command,
4519        cwd=cwd,
4520        user=user,
4521        password=password,
4522        ignore_retcode=ignore_retcode,
4523        output_encoding=output_encoding,
4524    )["stdout"]
4525
4526
4527def revision(
4528    cwd,
4529    rev="HEAD",
4530    short=False,
4531    user=None,
4532    password=None,
4533    ignore_retcode=False,
4534    output_encoding=None,
4535):
4536    """
4537    Returns the SHA1 hash of a given identifier (hash, branch, tag, HEAD, etc.)
4538
4539    cwd
4540        The path to the git checkout
4541
4542    rev : HEAD
4543        The revision
4544
4545    short : False
4546        If ``True``, return an abbreviated SHA1 git hash
4547
4548    user
4549        User under which to run the git command. By default, the command is run
4550        by the user under which the minion is running.
4551
4552    password
4553        Windows only. Required when specifying ``user``. This parameter will be
4554        ignored on non-Windows platforms.
4555
4556      .. versionadded:: 2016.3.4
4557
4558    ignore_retcode : False
4559        If ``True``, do not log an error to the minion log if the git command
4560        returns a nonzero exit status.
4561
4562        .. versionadded:: 2015.8.0
4563
4564    output_encoding
4565        Use this option to specify which encoding to use to decode the output
4566        from any git commands which are run. This should not be needed in most
4567        cases.
4568
4569        .. note::
4570            This should only be needed if the files in the repository were
4571            created with filenames using an encoding other than UTF-8 to handle
4572            Unicode characters.
4573
4574        .. versionadded:: 2018.3.1
4575
4576    CLI Example:
4577
4578    .. code-block:: bash
4579
4580        salt myminion git.revision /path/to/repo mybranch
4581    """
4582    cwd = _expand_path(cwd, user)
4583    command = ["git", "rev-parse"]
4584    if short:
4585        command.append("--short")
4586    command.append(rev)
4587    return _git_run(
4588        command,
4589        cwd=cwd,
4590        user=user,
4591        password=password,
4592        ignore_retcode=ignore_retcode,
4593        output_encoding=output_encoding,
4594    )["stdout"]
4595
4596
4597def rm_(
4598    cwd,
4599    filename,
4600    opts="",
4601    git_opts="",
4602    user=None,
4603    password=None,
4604    ignore_retcode=False,
4605    output_encoding=None,
4606):
4607    """
4608    Interface to `git-rm(1)`_
4609
4610    cwd
4611        The path to the git checkout
4612
4613    filename
4614        The location of the file/directory to remove, relative to ``cwd``
4615
4616        .. note::
4617            To remove a directory, ``-r`` must be part of the ``opts``
4618            parameter.
4619
4620    opts
4621        Any additional options to add to the command line, in a single string
4622
4623        .. note::
4624            On the Salt CLI, if the opts are preceded with a dash, it is
4625            necessary to precede them with ``opts=`` (as in the CLI examples
4626            below) to avoid causing errors with Salt's own argument parsing.
4627
4628    git_opts
4629        Any additional options to add to git command itself (not the ``rm``
4630        subcommand), in a single string. This is useful for passing ``-c`` to
4631        run git with temporary changes to the git configuration.
4632
4633        .. versionadded:: 2017.7.0
4634
4635        .. note::
4636            This is only supported in git 1.7.2 and newer.
4637
4638    user
4639        User under which to run the git command. By default, the command is run
4640        by the user under which the minion is running.
4641
4642    password
4643        Windows only. Required when specifying ``user``. This parameter will be
4644        ignored on non-Windows platforms.
4645
4646      .. versionadded:: 2016.3.4
4647
4648    ignore_retcode : False
4649        If ``True``, do not log an error to the minion log if the git command
4650        returns a nonzero exit status.
4651
4652        .. versionadded:: 2015.8.0
4653
4654    output_encoding
4655        Use this option to specify which encoding to use to decode the output
4656        from any git commands which are run. This should not be needed in most
4657        cases.
4658
4659        .. note::
4660            This should only be needed if the files in the repository were
4661            created with filenames using an encoding other than UTF-8 to handle
4662            Unicode characters.
4663
4664        .. versionadded:: 2018.3.1
4665
4666    .. _`git-rm(1)`: http://git-scm.com/docs/git-rm
4667
4668    CLI Examples:
4669
4670    .. code-block:: bash
4671
4672        salt myminion git.rm /path/to/repo foo/bar.py
4673        salt myminion git.rm /path/to/repo foo/bar.py opts='--dry-run'
4674        salt myminion git.rm /path/to/repo foo/baz opts='-r'
4675    """
4676    cwd = _expand_path(cwd, user)
4677    command = ["git"] + _format_git_opts(git_opts)
4678    command.append("rm")
4679    command.extend(_format_opts(opts))
4680    command.extend(["--", filename])
4681    return _git_run(
4682        command,
4683        cwd=cwd,
4684        user=user,
4685        password=password,
4686        ignore_retcode=ignore_retcode,
4687        output_encoding=output_encoding,
4688    )["stdout"]
4689
4690
4691def stash(
4692    cwd,
4693    action="save",
4694    opts="",
4695    git_opts="",
4696    user=None,
4697    password=None,
4698    ignore_retcode=False,
4699    output_encoding=None,
4700):
4701    """
4702    Interface to `git-stash(1)`_, returns the stdout from the git command
4703
4704    cwd
4705        The path to the git checkout
4706
4707    opts
4708        Any additional options to add to the command line, in a single string.
4709        Use this to complete the ``git stash`` command by adding the remaining
4710        arguments (i.e.  ``'save <stash comment>'``, ``'apply stash@{2}'``,
4711        ``'show'``, etc.).  Omitting this argument will simply run ``git
4712        stash``.
4713
4714    git_opts
4715        Any additional options to add to git command itself (not the ``stash``
4716        subcommand), in a single string. This is useful for passing ``-c`` to
4717        run git with temporary changes to the git configuration.
4718
4719        .. versionadded:: 2017.7.0
4720
4721        .. note::
4722            This is only supported in git 1.7.2 and newer.
4723
4724    user
4725        User under which to run the git command. By default, the command is run
4726        by the user under which the minion is running.
4727
4728    password
4729        Windows only. Required when specifying ``user``. This parameter will be
4730        ignored on non-Windows platforms.
4731
4732      .. versionadded:: 2016.3.4
4733
4734    ignore_retcode : False
4735        If ``True``, do not log an error to the minion log if the git command
4736        returns a nonzero exit status.
4737
4738        .. versionadded:: 2015.8.0
4739
4740    output_encoding
4741        Use this option to specify which encoding to use to decode the output
4742        from any git commands which are run. This should not be needed in most
4743        cases.
4744
4745        .. note::
4746            This should only be needed if the files in the repository were
4747            created with filenames using an encoding other than UTF-8 to handle
4748            Unicode characters.
4749
4750        .. versionadded:: 2018.3.1
4751
4752    .. _`git-stash(1)`: http://git-scm.com/docs/git-stash
4753
4754    CLI Examples:
4755
4756    .. code-block:: bash
4757
4758        salt myminion git.stash /path/to/repo save opts='work in progress'
4759        salt myminion git.stash /path/to/repo apply opts='stash@{1}'
4760        salt myminion git.stash /path/to/repo drop opts='stash@{1}'
4761        salt myminion git.stash /path/to/repo list
4762    """
4763    cwd = _expand_path(cwd, user)
4764    command = ["git"] + _format_git_opts(git_opts)
4765    command.extend(["stash", action])
4766    command.extend(_format_opts(opts))
4767    return _git_run(
4768        command,
4769        cwd=cwd,
4770        user=user,
4771        password=password,
4772        ignore_retcode=ignore_retcode,
4773        output_encoding=output_encoding,
4774    )["stdout"]
4775
4776
4777def status(cwd, user=None, password=None, ignore_retcode=False, output_encoding=None):
4778    """
4779    .. versionchanged:: 2015.8.0
4780        Return data has changed from a list of lists to a dictionary
4781
4782    Returns the changes to the repository
4783
4784    cwd
4785        The path to the git checkout
4786
4787    user
4788        User under which to run the git command. By default, the command is run
4789        by the user under which the minion is running.
4790
4791    password
4792        Windows only. Required when specifying ``user``. This parameter will be
4793        ignored on non-Windows platforms.
4794
4795      .. versionadded:: 2016.3.4
4796
4797    ignore_retcode : False
4798        If ``True``, do not log an error to the minion log if the git command
4799        returns a nonzero exit status.
4800
4801        .. versionadded:: 2015.8.0
4802
4803    output_encoding
4804        Use this option to specify which encoding to use to decode the output
4805        from any git commands which are run. This should not be needed in most
4806        cases.
4807
4808        .. note::
4809            This should only be needed if the files in the repository were
4810            created with filenames using an encoding other than UTF-8 to handle
4811            Unicode characters.
4812
4813        .. versionadded:: 2018.3.1
4814
4815    CLI Example:
4816
4817    .. code-block:: bash
4818
4819        salt myminion git.status /path/to/repo
4820    """
4821    cwd = _expand_path(cwd, user)
4822    state_map = {"M": "modified", "A": "new", "D": "deleted", "??": "untracked"}
4823    ret = {}
4824    command = ["git", "status", "-z", "--porcelain"]
4825    output = _git_run(
4826        command,
4827        cwd=cwd,
4828        user=user,
4829        password=password,
4830        ignore_retcode=ignore_retcode,
4831        output_encoding=output_encoding,
4832    )["stdout"]
4833    for line in output.split("\0"):
4834        try:
4835            state, filename = line.split(None, 1)
4836        except ValueError:
4837            continue
4838        ret.setdefault(state_map.get(state, state), []).append(filename)
4839    return ret
4840
4841
4842def submodule(
4843    cwd,
4844    command,
4845    opts="",
4846    git_opts="",
4847    user=None,
4848    password=None,
4849    identity=None,
4850    ignore_retcode=False,
4851    saltenv="base",
4852    output_encoding=None,
4853    **kwargs
4854):
4855    """
4856    .. versionchanged:: 2015.8.0
4857        Added the ``command`` argument to allow for operations other than
4858        ``update`` to be run on submodules, and deprecated the ``init``
4859        argument. To do a submodule update with ``init=True`` moving forward,
4860        use ``command=update opts='--init'``
4861
4862    Interface to `git-submodule(1)`_
4863
4864    cwd
4865        The path to the submodule
4866
4867    command
4868        Submodule command to run, see `git-submodule(1) <git submodule>` for
4869        more information. Any additional arguments after the command (such as
4870        the URL when adding a submodule) must be passed in the ``opts``
4871        parameter.
4872
4873        .. versionadded:: 2015.8.0
4874
4875    opts
4876        Any additional options to add to the command line, in a single string
4877
4878        .. note::
4879            On the Salt CLI, if the opts are preceded with a dash, it is
4880            necessary to precede them with ``opts=`` (as in the CLI examples
4881            below) to avoid causing errors with Salt's own argument parsing.
4882
4883    git_opts
4884        Any additional options to add to git command itself (not the
4885        ``submodule`` subcommand), in a single string. This is useful for
4886        passing ``-c`` to run git with temporary changes to the git
4887        configuration.
4888
4889        .. versionadded:: 2017.7.0
4890
4891        .. note::
4892            This is only supported in git 1.7.2 and newer.
4893
4894    init : False
4895        If ``True``, ensures that new submodules are initialized
4896
4897        .. deprecated:: 2015.8.0
4898            Pass ``init`` as the ``command`` parameter, or include ``--init``
4899            in the ``opts`` param with ``command`` set to update.
4900
4901    user
4902        User under which to run the git command. By default, the command is run
4903        by the user under which the minion is running.
4904
4905    password
4906        Windows only. Required when specifying ``user``. This parameter will be
4907        ignored on non-Windows platforms.
4908
4909      .. versionadded:: 2016.3.4
4910
4911    identity
4912        Path to a private key to use for ssh URLs
4913
4914        .. warning::
4915
4916            Unless Salt is invoked from the minion using ``salt-call``, the
4917            key(s) must be passphraseless. For greater security with
4918            passphraseless private keys, see the `sshd(8)`_ manpage for
4919            information on securing the keypair from the remote side in the
4920            ``authorized_keys`` file.
4921
4922            .. _`sshd(8)`: http://www.man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT
4923
4924        .. versionchanged:: 2015.8.7
4925
4926            Salt will no longer attempt to use passphrase-protected keys unless
4927            invoked from the minion using ``salt-call``, to prevent blocking
4928            waiting for user input.
4929
4930        Key can also be specified as a SaltStack file server URL, eg. salt://location/identity_file
4931
4932        .. versionchanged:: 2016.3.0
4933
4934    ignore_retcode : False
4935        If ``True``, do not log an error to the minion log if the git command
4936        returns a nonzero exit status.
4937
4938        .. versionadded:: 2015.8.0
4939
4940    saltenv
4941        The default salt environment to pull sls files from
4942
4943        .. versionadded:: 2016.3.1
4944
4945    output_encoding
4946        Use this option to specify which encoding to use to decode the output
4947        from any git commands which are run. This should not be needed in most
4948        cases.
4949
4950        .. note::
4951            This should only be needed if the files in the repository were
4952            created with filenames using an encoding other than UTF-8 to handle
4953            Unicode characters.
4954
4955        .. versionadded:: 2018.3.1
4956
4957    .. _`git-submodule(1)`: http://git-scm.com/docs/git-submodule
4958
4959    CLI Example:
4960
4961    .. code-block:: bash
4962
4963        # Update submodule and ensure it is initialized (before 2015.8.0)
4964        salt myminion git.submodule /path/to/repo/sub/repo init=True
4965        # Update submodule and ensure it is initialized (2015.8.0 and later)
4966        salt myminion git.submodule /path/to/repo/sub/repo update opts='--init'
4967
4968        # Rebase submodule (2015.8.0 and later)
4969        salt myminion git.submodule /path/to/repo/sub/repo update opts='--rebase'
4970
4971        # Add submodule (2015.8.0 and later)
4972        salt myminion git.submodule /path/to/repo/sub/repo add opts='https://mydomain.tld/repo.git'
4973
4974        # Unregister submodule (2015.8.0 and later)
4975        salt myminion git.submodule /path/to/repo/sub/repo deinit
4976    """
4977    kwargs = salt.utils.args.clean_kwargs(**kwargs)
4978    init_ = kwargs.pop("init", False)
4979    if kwargs:
4980        salt.utils.args.invalid_kwargs(kwargs)
4981
4982    cwd = _expand_path(cwd, user)
4983    if init_:
4984        raise SaltInvocationError(
4985            "The 'init' argument is no longer supported. Either set "
4986            "'command' to 'init', or include '--init' in the 'opts' "
4987            "argument and set 'command' to 'update'."
4988        )
4989    cmd = ["git"] + _format_git_opts(git_opts)
4990    cmd.extend(["submodule", command])
4991    cmd.extend(_format_opts(opts))
4992    return _git_run(
4993        cmd,
4994        cwd=cwd,
4995        user=user,
4996        password=password,
4997        identity=identity,
4998        ignore_retcode=ignore_retcode,
4999        saltenv=saltenv,
5000        output_encoding=output_encoding,
5001    )["stdout"]
5002
5003
5004def symbolic_ref(
5005    cwd,
5006    ref,
5007    value=None,
5008    opts="",
5009    git_opts="",
5010    user=None,
5011    password=None,
5012    ignore_retcode=False,
5013    output_encoding=None,
5014):
5015    """
5016    .. versionadded:: 2015.8.0
5017
5018    Interface to `git-symbolic-ref(1)`_
5019
5020    cwd
5021        The path to the git checkout
5022
5023    ref
5024        Symbolic ref to read/modify
5025
5026    value
5027        If passed, then the symbolic ref will be set to this value and an empty
5028        string will be returned.
5029
5030        If not passed, then the ref to which ``ref`` points will be returned,
5031        unless ``--delete`` is included in ``opts`` (in which case the symbolic
5032        ref will be deleted).
5033
5034    opts
5035        Any additional options to add to the command line, in a single string
5036
5037    git_opts
5038        Any additional options to add to git command itself (not the
5039        ``symbolic-refs`` subcommand), in a single string. This is useful for
5040        passing ``-c`` to run git with temporary changes to the git
5041        configuration.
5042
5043        .. versionadded:: 2017.7.0
5044
5045        .. note::
5046            This is only supported in git 1.7.2 and newer.
5047
5048    user
5049        User under which to run the git command. By default, the command is run
5050        by the user under which the minion is running.
5051
5052    password
5053        Windows only. Required when specifying ``user``. This parameter will be
5054        ignored on non-Windows platforms.
5055
5056      .. versionadded:: 2016.3.4
5057
5058    ignore_retcode : False
5059        If ``True``, do not log an error to the minion log if the git command
5060        returns a nonzero exit status.
5061
5062        .. versionadded:: 2015.8.0
5063
5064    output_encoding
5065        Use this option to specify which encoding to use to decode the output
5066        from any git commands which are run. This should not be needed in most
5067        cases.
5068
5069        .. note::
5070            This should only be needed if the files in the repository were
5071            created with filenames using an encoding other than UTF-8 to handle
5072            Unicode characters.
5073
5074        .. versionadded:: 2018.3.1
5075
5076    .. _`git-symbolic-ref(1)`: http://git-scm.com/docs/git-symbolic-ref
5077
5078    CLI Examples:
5079
5080    .. code-block:: bash
5081
5082        # Get ref to which HEAD is pointing
5083        salt myminion git.symbolic_ref /path/to/repo HEAD
5084        # Set/overwrite symbolic ref 'FOO' to local branch 'foo'
5085        salt myminion git.symbolic_ref /path/to/repo FOO refs/heads/foo
5086        # Delete symbolic ref 'FOO'
5087        salt myminion git.symbolic_ref /path/to/repo FOO opts='--delete'
5088    """
5089    cwd = _expand_path(cwd, user)
5090    command = ["git"] + _format_git_opts(git_opts)
5091    command.append("symbolic-ref")
5092    opts = _format_opts(opts)
5093    if value is not None and any(x in opts for x in ("-d", "--delete")):
5094        raise SaltInvocationError(
5095            "Value cannot be set for symbolic ref if -d/--delete is included in opts"
5096        )
5097    command.extend(opts)
5098    command.append(ref)
5099    if value:
5100        command.extend(value)
5101    return _git_run(
5102        command,
5103        cwd=cwd,
5104        user=user,
5105        password=password,
5106        ignore_retcode=ignore_retcode,
5107        output_encoding=output_encoding,
5108    )["stdout"]
5109
5110
5111def tag(
5112    cwd,
5113    name,
5114    ref="HEAD",
5115    message=None,
5116    opts="",
5117    git_opts="",
5118    user=None,
5119    password=None,
5120    ignore_retcode=False,
5121    output_encoding=None,
5122):
5123    """
5124    .. versionadded:: 2018.3.4
5125
5126    Interface to `git-tag(1)`_, adds and removes tags.
5127
5128    cwd
5129        The path to the main git checkout or a linked worktree
5130
5131    name
5132        Name of the tag
5133
5134    ref : HEAD
5135        Which ref to tag (defaults to local clone's HEAD)
5136
5137        .. note::
5138            This argument is ignored when either ``-d`` or ``--delete`` is
5139            present in the ``opts`` passed to this function.
5140
5141    message
5142        Optional message to include with the tag. If provided, an annotated tag
5143        will be created.
5144
5145    opts
5146        Any additional options to add to the command line, in a single string
5147
5148        .. note::
5149            Additionally, on the Salt CLI, if the opts are preceded with a
5150            dash, it is necessary to precede them with ``opts=`` (as in the CLI
5151            examples below) to avoid causing errors with Salt's own argument
5152            parsing.
5153
5154    git_opts
5155        Any additional options to add to git command itself (not the
5156        ``worktree`` subcommand), in a single string. This is useful for
5157        passing ``-c`` to run git with temporary changes to the git
5158        configuration.
5159
5160        .. note::
5161            This is only supported in git 1.7.2 and newer.
5162
5163    user
5164        User under which to run the git command. By default, the command is run
5165        by the user under which the minion is running.
5166
5167    password
5168        Windows only. Required when specifying ``user``. This parameter will be
5169        ignored on non-Windows platforms.
5170
5171    ignore_retcode : False
5172        If ``True``, do not log an error to the minion log if the git command
5173        returns a nonzero exit status.
5174
5175    output_encoding
5176        Use this option to specify which encoding to use to decode the output
5177        from any git commands which are run. This should not be needed in most
5178        cases.
5179
5180        .. note::
5181            This should only be needed if the files in the repository were
5182            created with filenames using an encoding other than UTF-8 to handle
5183            Unicode characters.
5184
5185    .. _`git-tag(1)`: http://git-scm.com/docs/git-tag
5186
5187    CLI Example:
5188
5189    .. code-block:: bash
5190
5191        # Create an non-annotated tag
5192        salt myminion git.tag /path/to/repo v1.2
5193        # Create an annotated tag
5194        salt myminion git.tag /path/to/repo v1.2 message='Version 1.2'
5195        # Delete the tag
5196        salt myminion git.tag /path/to/repo v1.2 opts='-d'
5197    """
5198    cwd = _expand_path(cwd, user)
5199    command = ["git"] + _format_git_opts(git_opts)
5200    command.append("tag")
5201    # Don't add options for annotated tags, since we'll automatically add them
5202    # if a message was passed. This keeps us from blocking on input, as passing
5203    # these options without a separate message option would launch an editor.
5204    formatted_opts = [x for x in _format_opts(opts) if x not in ("-a", "--annotate")]
5205    # Make sure that the message was not passed in the opts
5206    if any(x == "-m" or "--message" in x for x in formatted_opts):
5207        raise SaltInvocationError(
5208            'Tag messages must be passed in the "message" argument'
5209        )
5210    command.extend(formatted_opts)
5211    command.append(name)
5212    if "-d" not in formatted_opts and "--delete" not in formatted_opts:
5213        command.append(ref)
5214    return _git_run(
5215        command,
5216        cwd=cwd,
5217        user=user,
5218        password=password,
5219        ignore_retcode=ignore_retcode,
5220        redirect_stderr=True,
5221        output_encoding=output_encoding,
5222    )["stdout"]
5223
5224
5225def version(versioninfo=False):
5226    """
5227    .. versionadded:: 2015.8.0
5228
5229    Returns the version of Git installed on the minion
5230
5231    versioninfo : False
5232        If ``True``, return the version in a versioninfo list (e.g. ``[2, 5,
5233        0]``)
5234
5235    CLI Example:
5236
5237    .. code-block:: bash
5238
5239        salt myminion git.version
5240    """
5241    contextkey = "git.version"
5242    contextkey_info = "git.versioninfo"
5243    if contextkey not in __context__:
5244        try:
5245            version_ = _git_run(["git", "--version"])["stdout"]
5246        except CommandExecutionError as exc:
5247            log.error("Failed to obtain the git version (error follows):\n%s", exc)
5248            version_ = "unknown"
5249        try:
5250            __context__[contextkey] = version_.split()[-1]
5251        except IndexError:
5252            # Somehow git --version returned no stdout while not raising an
5253            # error. Should never happen but we should still account for this
5254            # possible edge case.
5255            log.error("Running 'git --version' returned no stdout")
5256            __context__[contextkey] = "unknown"
5257    if not versioninfo:
5258        return __context__[contextkey]
5259    if contextkey_info not in __context__:
5260        # Set ptr to the memory location of __context__[contextkey_info] to
5261        # prevent repeated dict lookups
5262        ptr = __context__.setdefault(contextkey_info, [])
5263        for part in __context__[contextkey].split("."):
5264            try:
5265                ptr.append(int(part))
5266            except ValueError:
5267                ptr.append(part)
5268    return __context__[contextkey_info]
5269
5270
5271def worktree_add(
5272    cwd,
5273    worktree_path,
5274    ref=None,
5275    reset_branch=None,
5276    force=None,
5277    detach=False,
5278    opts="",
5279    git_opts="",
5280    user=None,
5281    password=None,
5282    ignore_retcode=False,
5283    output_encoding=None,
5284    **kwargs
5285):
5286    """
5287    .. versionadded:: 2015.8.0
5288
5289    Interface to `git-worktree(1)`_, adds a worktree
5290
5291    cwd
5292        The path to the git checkout
5293
5294    worktree_path
5295        Path to the new worktree. Can be either absolute, or relative to
5296        ``cwd``.
5297
5298    branch
5299        Name of new branch to create. If omitted, will be set to the basename
5300        of the ``worktree_path``. For example, if the ``worktree_path`` is
5301        ``/foo/bar/baz``, then ``branch`` will be ``baz``.
5302
5303    ref
5304        Name of the ref on which to base the new worktree. If omitted, then
5305        ``HEAD`` is use, and a new branch will be created, named for the
5306        basename of the ``worktree_path``. For example, if the
5307        ``worktree_path`` is ``/foo/bar/baz`` then a new branch ``baz`` will be
5308        created, and pointed at ``HEAD``.
5309
5310    reset_branch : False
5311        If ``False``, then `git-worktree(1)`_ will fail to create the worktree
5312        if the targeted branch already exists. Set this argument to ``True`` to
5313        reset the targeted branch to point at ``ref``, and checkout the
5314        newly-reset branch into the new worktree.
5315
5316    force : False
5317        By default, `git-worktree(1)`_ will not permit the same branch to be
5318        checked out in more than one worktree. Set this argument to ``True`` to
5319        override this.
5320
5321    opts
5322        Any additional options to add to the command line, in a single string
5323
5324        .. note::
5325            On the Salt CLI, if the opts are preceded with a dash, it is
5326            necessary to precede them with ``opts=`` to avoid causing errors
5327            with Salt's own argument parsing.
5328
5329            All CLI options for adding worktrees as of Git 2.5.0 are already
5330            supported by this function as of Salt 2015.8.0, so using this
5331            argument is unnecessary unless new CLI arguments are added to
5332            `git-worktree(1)`_ and are not yet supported in Salt.
5333
5334    git_opts
5335        Any additional options to add to git command itself (not the
5336        ``worktree`` subcommand), in a single string. This is useful for
5337        passing ``-c`` to run git with temporary changes to the git
5338        configuration.
5339
5340        .. versionadded:: 2017.7.0
5341
5342        .. note::
5343            This is only supported in git 1.7.2 and newer.
5344
5345    user
5346        User under which to run the git command. By default, the command is run
5347        by the user under which the minion is running.
5348
5349    password
5350        Windows only. Required when specifying ``user``. This parameter will be
5351        ignored on non-Windows platforms.
5352
5353      .. versionadded:: 2016.3.4
5354
5355    ignore_retcode : False
5356        If ``True``, do not log an error to the minion log if the git command
5357        returns a nonzero exit status.
5358
5359        .. versionadded:: 2015.8.0
5360
5361    output_encoding
5362        Use this option to specify which encoding to use to decode the output
5363        from any git commands which are run. This should not be needed in most
5364        cases.
5365
5366        .. note::
5367            This should only be needed if the files in the repository were
5368            created with filenames using an encoding other than UTF-8 to handle
5369            Unicode characters.
5370
5371        .. versionadded:: 2018.3.1
5372
5373    .. _`git-worktree(1)`: http://git-scm.com/docs/git-worktree
5374
5375    CLI Examples:
5376
5377    .. code-block:: bash
5378
5379        salt myminion git.worktree_add /path/to/repo/main ../hotfix ref=origin/master
5380        salt myminion git.worktree_add /path/to/repo/main ../hotfix branch=hotfix21 ref=v2.1.9.3
5381    """
5382    _check_worktree_support()
5383    kwargs = salt.utils.args.clean_kwargs(**kwargs)
5384    branch_ = kwargs.pop("branch", None)
5385    if kwargs:
5386        salt.utils.args.invalid_kwargs(kwargs)
5387
5388    cwd = _expand_path(cwd, user)
5389    if branch_ and detach:
5390        raise SaltInvocationError("Only one of 'branch' and 'detach' is allowed")
5391
5392    command = ["git"] + _format_git_opts(git_opts)
5393    command.extend(["worktree", "add"])
5394    if detach:
5395        if force:
5396            log.warning(
5397                "'force' argument to git.worktree_add is ignored when detach=True"
5398            )
5399        command.append("--detach")
5400    else:
5401        if not branch_:
5402            branch_ = os.path.basename(worktree_path)
5403        command.extend(["-B" if reset_branch else "-b", branch_])
5404        if force:
5405            command.append("--force")
5406    command.extend(_format_opts(opts))
5407    command.append(worktree_path)
5408    if ref:
5409        command.append(ref)
5410    # Checkout message goes to stderr
5411    return _git_run(
5412        command,
5413        cwd=cwd,
5414        user=user,
5415        password=password,
5416        ignore_retcode=ignore_retcode,
5417        redirect_stderr=True,
5418        output_encoding=output_encoding,
5419    )["stdout"]
5420
5421
5422def worktree_prune(
5423    cwd,
5424    dry_run=False,
5425    verbose=True,
5426    expire=None,
5427    opts="",
5428    git_opts="",
5429    user=None,
5430    password=None,
5431    ignore_retcode=False,
5432    output_encoding=None,
5433):
5434    """
5435    .. versionadded:: 2015.8.0
5436
5437    Interface to `git-worktree(1)`_, prunes stale worktree administrative data
5438    from the gitdir
5439
5440    cwd
5441        The path to the main git checkout or a linked worktree
5442
5443    dry_run : False
5444        If ``True``, then this function will report what would have been
5445        pruned, but no changes will be made.
5446
5447    verbose : True
5448        Report all changes made. Set to ``False`` to suppress this output.
5449
5450    expire
5451        Only prune unused worktree data older than a specific period of time.
5452        The date format for this parameter is described in the documentation
5453        for the ``gc.pruneWorktreesExpire`` config param in the
5454        `git-config(1)`_ manpage.
5455
5456    opts
5457        Any additional options to add to the command line, in a single string
5458
5459        .. note::
5460            On the Salt CLI, if the opts are preceded with a dash, it is
5461            necessary to precede them with ``opts=`` to avoid causing errors
5462            with Salt's own argument parsing.
5463
5464            All CLI options for pruning worktrees as of Git 2.5.0 are already
5465            supported by this function as of Salt 2015.8.0, so using this
5466            argument is unnecessary unless new CLI arguments are added to
5467            `git-worktree(1)`_ and are not yet supported in Salt.
5468
5469    git_opts
5470        Any additional options to add to git command itself (not the
5471        ``worktree`` subcommand), in a single string. This is useful for
5472        passing ``-c`` to run git with temporary changes to the git
5473        configuration.
5474
5475        .. versionadded:: 2017.7.0
5476
5477        .. note::
5478            This is only supported in git 1.7.2 and newer.
5479
5480    user
5481        User under which to run the git command. By default, the command is run
5482        by the user under which the minion is running.
5483
5484    password
5485        Windows only. Required when specifying ``user``. This parameter will be
5486        ignored on non-Windows platforms.
5487
5488      .. versionadded:: 2016.3.4
5489
5490    ignore_retcode : False
5491        If ``True``, do not log an error to the minion log if the git command
5492        returns a nonzero exit status.
5493
5494        .. versionadded:: 2015.8.0
5495
5496    output_encoding
5497        Use this option to specify which encoding to use to decode the output
5498        from any git commands which are run. This should not be needed in most
5499        cases.
5500
5501        .. note::
5502            This should only be needed if the files in the repository were
5503            created with filenames using an encoding other than UTF-8 to handle
5504            Unicode characters.
5505
5506        .. versionadded:: 2018.3.1
5507
5508    .. _`git-worktree(1)`: http://git-scm.com/docs/git-worktree
5509    .. _`git-config(1)`: http://git-scm.com/docs/git-config/2.5.1
5510
5511    CLI Examples:
5512
5513    .. code-block:: bash
5514
5515        salt myminion git.worktree_prune /path/to/repo
5516        salt myminion git.worktree_prune /path/to/repo dry_run=True
5517        salt myminion git.worktree_prune /path/to/repo expire=1.day.ago
5518    """
5519    _check_worktree_support()
5520    cwd = _expand_path(cwd, user)
5521    command = ["git"] + _format_git_opts(git_opts)
5522    command.extend(["worktree", "prune"])
5523    if dry_run:
5524        command.append("--dry-run")
5525    if verbose:
5526        command.append("--verbose")
5527    if expire:
5528        command.extend(["--expire", expire])
5529    command.extend(_format_opts(opts))
5530    return _git_run(
5531        command,
5532        cwd=cwd,
5533        user=user,
5534        password=password,
5535        ignore_retcode=ignore_retcode,
5536        output_encoding=output_encoding,
5537    )["stdout"]
5538
5539
5540def worktree_rm(cwd, user=None, output_encoding=None):
5541    """
5542    .. versionadded:: 2015.8.0
5543
5544    Recursively removes the worktree located at ``cwd``, returning ``True`` if
5545    successful. This function will attempt to determine if ``cwd`` is actually
5546    a worktree by invoking :py:func:`git.is_worktree
5547    <salt.modules.git.is_worktree>`. If the path does not correspond to a
5548    worktree, then an error will be raised and no action will be taken.
5549
5550    .. warning::
5551
5552        There is no undoing this action. Be **VERY** careful before running
5553        this function.
5554
5555    cwd
5556        Path to the worktree to be removed
5557
5558    user
5559        Used for path expansion when ``cwd`` is not an absolute path. By
5560        default, when ``cwd`` is not absolute, the path will be assumed to be
5561        relative to the home directory of the user under which the minion is
5562        running. Setting this option will change the home directory from which
5563        path expansion is performed.
5564
5565    output_encoding
5566        Use this option to specify which encoding to use to decode the output
5567        from any git commands which are run. This should not be needed in most
5568        cases.
5569
5570        .. note::
5571            This should only be needed if the files in the repository were
5572            created with filenames using an encoding other than UTF-8 to handle
5573            Unicode characters.
5574
5575        .. versionadded:: 2018.3.1
5576
5577    CLI Examples:
5578
5579    .. code-block:: bash
5580
5581        salt myminion git.worktree_rm /path/to/worktree
5582    """
5583    _check_worktree_support()
5584    cwd = _expand_path(cwd, user)
5585    if not os.path.exists(cwd):
5586        raise CommandExecutionError(cwd + " does not exist")
5587    elif not is_worktree(cwd, output_encoding=output_encoding):
5588        raise CommandExecutionError(cwd + " is not a git worktree")
5589    try:
5590        salt.utils.files.rm_rf(cwd)
5591    except Exception as exc:  # pylint: disable=broad-except
5592        raise CommandExecutionError("Unable to remove {}: {}".format(cwd, exc))
5593    return True
5594