可以用git别名覆盖git命令吗?

3zwjbxry  于 2022-12-25  发布在  Git
关注(0)|答案(6)|浏览(195)

我的~/.gitconfig是:

[alias]
        commit = "!sh commit.sh"

但是,当我输入 git commit 时,脚本没有被调用。
有没有可能,或者我得用另一个化名?

sycxhyv7

sycxhyv71#

这不可能

这是我的git克隆版本。

static int run_argv(int *argcp, const char ***argv)
{
    int done_alias = 0;

    while (1) {
        /* See if it's an internal command */
        handle_internal_command(*argcp, *argv);

        /* .. then try the external ones */
        execv_dashed_external(*argv);

        /* It could be an alias -- this works around the insanity
         * of overriding "git log" with "git show" by having
         * alias.log = show
         */
        if (done_alias || !handle_alias(argcp, argv))
            break;
        done_alias = 1;
    }

    return done_alias;
}

所以这是不可能的。(handle_internal_command如果找到命令就调用exit)。
您可以通过更改行的顺序并在handle_alias找到别名时使其调用exit来在源代码中解决这个问题。

ttcibm8c

ttcibm8c2#

我选择使用bash函数来解决这个问题,如果我调用git clone,它会将调用重定向到git cl,这是我的别名,添加了一些开关。

function git {
  if [[ "$1" == "clone" && "$@" != *"--help"* ]]; then
    shift 1
    command git cl "$@"
  else
    command git "$@"
  fi
}
g9icjywg

g9icjywg3#

如前所述,不可能使用git别名来覆盖git命令,但是可以使用shell别名来覆盖git命令。对于任何POSIX shell(即不是MS cmd),编写一个简单的可执行脚本来执行所需的修改行为,并设置shell别名。在我的.bashrc(Linux)和.bash_profile(Mac)中,我有

export PATH="~/bin:$PATH"
...
alias git='my-git'

在我的~/bin文件夹中,我有一个可执行的Perl脚本my-git,它检查第一个参数(即git命令)是否为clone

#!/usr/bin/env perl
use strict;
use warnings;
my $path_to_git = '/usr/local/bin/git';
exit(system($path_to_git, @ARGV))
    if @ARGV < 2 or $ARGV[0] ne 'clone';
# Override git-clone here...

我的是一个更可配置的一点,但你明白的想法。

31moq8wy

31moq8wy4#

    • 不仅不可能,而且是WONTFIX**

在2009年
Hamano replies
目前git不允许别名覆盖内置函数,我理解这背后的原因,但我怀疑这是否过于保守。
它不是。
大多数shell支持用别名覆盖命令,我不知道为什么git需要比shell更保守。
因为sane shell在脚本中使用时不会展开别名,并且提供了一种即使从命令行也能消除别名的简便方法。

$ alias ls='ls -aF'
$ echo ls >script
$ chmod +x script

并比较:

$ ./script
$ ls
$ /bin/ls
knsnq2tg

knsnq2tg5#

现在,我通过编写下面的~/bin/git Package 器解决了这个问题(好吧,“解决了它”...),该 Package 器检查~/bin/git-clone,并调用that而不是内置函数。
[NOTE:我为任何“聪明”的害羞表示歉意,但是在您通过两个helper函数之后--一个用于扩展符号链接,一个用于搜索$PATH以查找要 Package 的可执行文件--实际的脚本本身只是Three Lines of Code™......所以我想我并不感到抱歉,呵呵!]

#!/usr/bin/env bash

###########################
###  UTILITY FUNCTIONS  ###  ...from my .bashrc
###########################
#
# deref "/path/with/links/to/symlink"
#   - Returns physical path for specified target
#
# __SUPER__
#   - Returns next "$0" in $PATH (that isn't me, or a symlink to me...)

deref() {
  ( # Wrap 'cd's in a sub-shell
    local target="$1"
    local counter=0

    # If the argument itself is a link [to a link, to a link...]
    # NOTE: readlink(1) is not defined by POSIX, but has been shown to
    #  work on at least MacOS X, CentOS, Ubuntu, openSUSE, and OpenBSD
    while [[ -L "$target" ]]; do
        [[ $((++counter)) -ge 30 ]] && return 1
        cd "${target%/*}"; target="$(readlink "$target")"
    done

    # Expand parent directory hierarchy
    cd "${target%/*}" 2>/dev/null \
      && echo "$(pwd -P)/${target##*/}" \
      || echo "$([[ $target != /* ]] && echo "$(pwd -P)/")$target"
  )
}

__SUPER__() {
  local cmd="${1:-${0##*/}}"
  local me="$(deref "$0")"

  # NOTE: We only consider symlinks...  We could check for hardlinks by
  #       comparing device+inode, but stat(1) has portability problems

  local IFS=":"
  for d in $PATH; do
    [[ -x "$d/$cmd" ]] && [[ "$(deref "$d/$cmd")" != "$me" ]] \
      && { echo "$d/$cmd"; return; }
  done

  # else...
  return 1
}

########################################################################

# (1) First, figure out which '$0' we *WOULD* have run...

GIT="$(__SUPER__)" || { echo "${0##*/}: command not found" >&2; exit 1; }

# (2) If we have a "~/bin/git-${command}" wrapper, then
#     prepend '.../libexec/git-core' to $PATH and run it

[[ -f "${HOME}/bin/git-$1" ]] &&
  PATH="$PATH:$( "$GIT" --exec-path )" \
    exec "${HOME}/bin/git-$1" "${@:2}"

# (3) Else fall back to the regular 'git'

exec "$GIT" "$@"
deyfvvtc

deyfvvtc6#

这里还有另一个变通方法,它不依赖于 * 实际上 * 覆盖任何东西,而是依赖于滥用自动完成。这感觉比 Package git更安全,更透明。
由于我从不键入完整的命令名,因此定义别名作为要覆盖的命令的前缀就足够了。
例如,要用别名覆盖git show-branch,并且知道我通常键入git show-<Tab>,我定义了show-br别名来定制show-branch行为。
对于OP的示例git commit,我通常键入git com<Tab>,因此git comm将是正确的解决方案。
这种策略有几个优点:

  • 很明显,您没有调用“vanilla”命令。
  • 如果前缀足够长:
  • 它不会修改您的工作流
  • 很明显你在破坏哪个指挥部
  • 原始命令仍然很容易访问

相关问题