web-dev-qa-db-ja.com

gitエイリアスでgitコマンドをオーバーライドすることは可能ですか?

私の〜/ .gitconfigは次のとおりです。

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

ただし、git commitと入力すると、スクリプトは呼び出されません。

それは可能ですか、それとも別のエイリアス名を使用する必要がありますか?

49
noisy

それは不可能です

これは私の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_commandexitを呼び出します)。

行の順序を変更し、エイリアスが見つかった場合はhandle_aliasexitを呼び出すことで、ソースでこれを修正できます。

41
alternative

すでに述べたように、gitエイリアスを使用してgitコマンドをオーバーライドすることはできません。ただし、シェルエイリアスを使用してgitコマンドをオーバーライドすることは可能です。 POSIXyシェル(つまり、MS cmdではない)の場合、目的の変更された動作を実行し、シェルエイリアスを設定する単純な実行可能スクリプトを記述します。私の.bashrc(Linux)と.bash_profile(Mac)には

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

私の~/binフォルダーには、最初の引数(つまり、gitコマンド)がcloneであるかどうかをチェックするmy-gitという実行可能なPerlスクリプトがあります。基本的には次のようになります。

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

鉱山はもう少し構成可能ですが、あなたはその考えを理解します。

22
David Mertens

私はこれをbash関数で解決することを選びました。 git cloneを呼び出すと、呼び出しがgit clにリダイレクトされます。これは、いくつかのスイッチが追加されたエイリアスです。

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

不可能なだけでなく、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、および組み込みの代わりにthatを呼び出します。

[注:「巧妙な」bash-ismについてはお詫びしますが、2つのヘルパー関数(1つはシンボリックリンクを展開するため、もう1つは$ PATHでラップされている実行可能ファイルを検索するため)を通過した後、実際のスクリプト自体は3行になります。 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" "$@"
0
Dabe Murphy