Можно ли переопределить команду git псевдонимом git?

Мой ~/.gitconfig это:

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

Однако, когда я набираю git commit, скрипт не вызывается.

Это возможно, или я должен использовать другое псевдоним?

6 ответов

Решение

Это невозможно

Это из моего клона 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 если он найдет псевдоним.

Я решил решить эту проблему с помощью функции bash. Если я позвоню git clone, он перенаправит вызов на git cl, который является моим псевдонимом с некоторыми добавленными переключателями.

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

Как уже упоминалось, невозможно использовать псевдоним git для переопределения команды git. Однако можно переопределить команду git, используя псевдоним оболочки. Для любой оболочки POSIXy (т.е. не MS cmd), напишите простой исполняемый скрипт, который выполняет желаемое измененное поведение и установите псевдоним оболочки. В моем .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...

Мой немного более настраиваемый, но вы поняли идею.

Не только невозможно, но и WONTFIX в 2009 г. http://git.661346.n2.nabble.com/allowing-aliases-to-override-builtins-to-support-default-options-td2438491.html

Хамано отвечает:

В настоящее время git не позволяет псевдонимам переопределять встроенные функции. Я понимаю причину этого, но мне интересно, чрезмерно ли она консервативна.

Это не.

Большинство оболочек поддерживают переопределение команд с псевдонимами, и я не уверен, почему git должен быть более консервативным, чем оболочка.

Потому что нормальные оболочки не раскрывают псевдонимы при использовании в сценарии и дают удобный способ победить псевдоним даже из командной строки.

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

и сравните:

$ ./script
$ ls
$ /bin/ls

FWIW, я решил это (хорошо, "обошел"...), написав следующее~/bin/git оболочка, которая проверяет, например, ~/bin/git-clone, и вызывает это вместо встроенного.

[ПРИМЕЧАНИЕ: я прошу прощения за любые "умные" bash-isms, но после того, как вы пройдете две вспомогательные функции - одну для расширения символических ссылок, а другую для поиска в вашем $PATH оборачиваемого исполняемого файла - сам скрипт представляет собой всего три строки 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" "$@"

Вот еще один обходной путь, который не основан на фактическом переопределении чего-либо, а вместо этого основан на злоупотреблении автозаполнением . Это кажется менее опасным и более прозрачным, чем обертывание git.

Поскольку я никогда не ввожу полные имена команд, достаточно определить псевдоним, который является префиксом переопределяемой команды.

Например, чтобы переопределитьgit show-branchс псевдонимом и зная, что я обычно печатаюgit show-<Tab>, я определяюshow-brпсевдоним для настройкиshow-branchповедение.

Для примера ОПgit commitя обычно печатаюgit com<Tab>такgit commбудет правильным обходным путем.

Эта стратегия имеет ряд преимуществ:

  • Понятно, что вы не вызываете «ванильную» команду.
  • Если ваш префикс достаточно длинный:
    • Это не изменяет ваш рабочий процесс
    • Ясно, какую команду вы подрываете
  • Исходная команда по-прежнему легко доступна
Другие вопросы по тегам