Можно ли переопределить команду 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
будет правильным обходным путем.
Эта стратегия имеет ряд преимуществ:
- Понятно, что вы не вызываете «ванильную» команду.
- Если ваш префикс достаточно длинный:
- Это не изменяет ваш рабочий процесс
- Ясно, какую команду вы подрываете
- Исходная команда по-прежнему легко доступна