是否可以通过 git 别名覆盖 git 命令?

发布于 2024-09-15 09:31:38 字数 156 浏览 1 评论 0原文

我的 ~/.gitconfig 是:

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

但是,当我输入 git commit 时,不会调用脚本。

是否可能,或者我必须使用另一个别名?

my ~/.gitconfig is:

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

However, when I type git commit, script is not called.

Is it possible, or I have to use another alias name?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

洒一地阳光 2024-09-22 09:31:38

这是不可能的

这是来自我的 git.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 来修复源代码中的此问题。

It is NOT POSSIBLE

This is from my clone of git.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;
}

So its not possible. (handle_internal_command calls exit if it finds the command).

You could fix this in your sources by changing the order of the lines and making handle_alias call exit if it finds the alias.

碍人泪离人颜 2024-09-22 09:31:38

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

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

I opted to solve this with a bash function. If I call git clone, it will redirect the call to git cl, which is my alias with some added switches.

function git {
  if [[ "$1" == "clone" && "$@" != *"--help"* ]]; then
    shift 1
    command git cl "$@"
  else
    command git "$@"
  fi
}
怼怹恏 2024-09-22 09:31:38

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

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

在我的 ~/bin 文件夹中,我有一个名为 < code>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...

我的可配置性更高一点,但你明白了。

As already mentioned, it is not possible to use a git alias to override a git command. However, it is possible to override a git command using a shell alias. For any POSIXy shell (i.e. not MS cmd), write a simple executable script that performs the desired modified behavior and set a shell alias. In my .bashrc (Linux) and .bash_profile (Mac) I have

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

In my ~/bin folder I have an executable Perl script called my-git that checks if the first argument (i.e. the git command) is clone. It looks essentially like this:

#!/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...

Mine is a little more configurable, but you get the idea.

瑶笙 2024-09-22 09:31:38

不仅不可能,而且 WONTFIX

2009 年 http://git.661346.n2.nabble.com/allowing-aliases-to-override-builtins-to-support-default-options-td2438491.html< /a>

滨野回复

<块引用>

目前 git 不允许别名覆盖内置命令。我
理解这背后的原因,但我想知道这是否太过分了
保守。

事实并非如此。

<块引用>

大多数 shell 都支持使用别名覆盖命令,但我不确定
为什么 git 需要比 shell 更保守。

因为 sane shell 在脚本中使用时不会扩展别名,并且
提供了一种即使从命令行也可以击败别名的便捷方法。

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

并比较:

<前><代码>$ ./脚本
$ls
$ /bin/ls

Not only not possible, but also WONTFIX

In 2009 http://git.661346.n2.nabble.com/allowing-aliases-to-override-builtins-to-support-default-options-td2438491.html

Hamano replies:

Currently git does not allow aliases to override builtins. I
understand the reasoning behind this, but I wonder if it's overly
conservative.

It is not.

Most shells support overriding commands with aliases, and I'm not sure
why git needs to be more conservative than the shell.

Because sane shells do not expand aliases when used in a script, and
gives a handy way to defeat the alias even from the command line.

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

and compare:

$ ./script
$ ls
$ /bin/ls
萌辣 2024-09-22 09:31:38

FWIW,我通过编写以下 ~/bin/git 包装器解决了这个问题(好吧,“解决了它”...),它检查例如 ~/bin/git-clone,并调用 that 而不是内置函数。

[注意:我为任何“聪明”的 bash-isms 表示歉意,但是在您通过两个辅助函数之后 - 一个用于扩展符号链接,另一个用于在 $PATH 中搜索正在包装的可执行文件 - 实际的脚本本身只是三个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" "$@"

FWIW, I solved this (okay, "worked around it"...) by writing the following ~/bin/git wrapper, which checks for, e.g., ~/bin/git-clone, and calls that instead of the built-in.

[NOTE: I apologize for any "clever" bash-isms, but after you get past the two helper functions — one to expand symlinks and one to search your $PATH for the executable being wrapped — the actual script itself is just Three Lines of Code™... So I guess I'm not sorry after all, hehe!]

#!/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" "$@"
我爱人 2024-09-22 09:31:38

这是另一种解决方法,它并不依赖于实际上覆盖任何内容,而是依赖于滥用自动完成。这感觉比包装 git 更危险、更透明。

由于我从不键入完整的命令名称,因此定义要覆盖的命令的前缀别名就足够了。

例如,要使用别名覆盖 git show-branch,并且知道我通常输入 git show-,我定义了 show-br< /code> 别名来自定义 show-branch 行为。

对于OP的示例 git commit 我通常输入 git com 所以 git comm 将是正确的解决方法。

这种策略有几个优点:

  • 很明显,您没有调用“普通”命令。
  • 如果您的前缀足够长:
    • 它不会修改您的工作流程
    • 很清楚您正在破坏哪个命令
  • 原始命令仍然可以轻松访问

Here’s yet another workaround, that does not rely on actually overriding anything, but relies on abusing autocomplete instead. This feels less dangerous and more transparent than wrapping git.

As I never type the full command names, it is enough to define an alias that is a prefix of the command to override.

For example, to override git show-branch with an alias, and knowing I usually type git show-<Tab>, I define the show-br alias to customise the show-branch behaviour.

For OP’s example git commit I usually type git com<Tab> so git comm would be correct workaround.

This strategy has several advantages:

  • It is clear you are not calling the “vanilla” command.
  • If your prefix is long enough:
    • It does not modify your workflow
    • It is clear which command you’re subverting
  • The original command is still easily accessible
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文