Alias ​​Git z parametrami pozycyjnymi

261

Zasadniczo próbuję uzyskać alias:

git files 9fa3

... aby wykonać polecenie:

git diff --name-status 9fa3^ 9fa3

ale wydaje się, że git nie przekazuje parametrów pozycyjnych do polecenia aliasu. Próbowałem:

[alias]
    files = "!git diff --name-status $1^ $1"
    files = "!git diff --name-status {1}^ {1}"

... i kilka innych, ale te nie działały.

Przypadek zdegenerowany to:

$ git echo_reverse_these_params a b c d e
e d c b a

... jak mogę to zrobić?

użytkownik400575
źródło
17
Zauważ, że w git 1.8.2.1 można to zrobić bez funkcji powłoki (twoje oryginalne podejście $1powinno działać).
Eimantas
7
@Eimantas Czy chciałbyś opracować odpowiedź? To nie działa dla mnie i nie mogę znaleźć żadnej dokumentacji na ten temat.
pavon
@Eimantas nie ma nic na ten temat w informacjach o wydaniu .
Knu
1
Potwierdzam, że mogę uruchamiać polecenia powłoki z argumentami bez żadnych shenaniganów w Git 2.11.
anarcat

Odpowiedzi:

365

Najbardziej oczywistym sposobem jest użycie funkcji powłoki:

[alias]
    files = "!f() { git diff --name-status \"$1^\" \"$1\"; }; f"

Alias ​​bez !jest traktowany jako polecenie Git; np commit-all = commit -a.

Dzięki !, jest uruchamiany jako osobne polecenie w powłoce, co pozwala na użycie silniejszej magii w ten sposób.

UPD
Ponieważ polecenia są wykonywane w katalogu głównym repozytorium, możesz używać ${GIT_PREFIX}zmiennej w odniesieniu do nazw plików w poleceniach

Cascabel
źródło
8
Dzięki, wygląda to dokładnie tak: [alias] files = "! F () {echo 3 $ 2 $ 1 $;}; f"; $ git files abc => cba
user400575
1
@ KohányiRóbert: To właściwie nie jest pytanie skryptu powłoki; to jest szczególna konfiguracja git. Alias ​​bez !jest traktowany jako polecenie Git; np commit-all = commit -a. Dzięki !, jest uruchamiany jako osobne polecenie w powłoce, co pozwala na użycie silniejszej magii w ten sposób.
Cascabel,
40
Uważaj, !będzie działać w katalogu głównym repozytorium, więc użycie względnych ścieżek podczas wywoływania twojego aliasu nie da oczekiwanych rezultatów.
Drealmer,
4
@RobertDailey Nie psuje go, tylko go nie implementuje. Zobacz stackoverflow.com/questions/342969/…, aby dowiedzieć się, jak go dodać.
Cascabel
3
Uwaga : To nie cytuje argumentów (co jest ogólnie niebezpieczne). Ponadto funkcja nie jest potrzebna. Zobacz moją odpowiedź, aby uzyskać więcej wyjaśnień.
Tom Hale,
96

Możesz także odwoływać się shbezpośrednio (zamiast tworzenia funkcji):

[alias]
        files = !sh -c 'git diff --name-status $1^ $1' -

(Uwaga kreska na końcu linii - będziesz tego potrzebować.)

mipadi
źródło
8
Jeśli udostępniasz polecenie, prawdopodobnie chcesz go użyć sh, ponieważ jest to sama w sobie powłoka i jest dostępna w zdecydowanej większości systemów. Korzystanie z domyślnej powłoki działa tylko wtedy, gdy polecenie działa tak, jak napisano dla wszystkich powłok.
nomothetis
12
Wolę --się -jak to jest bardziej znane i mniej prawdopodobne, aby przypadkowo średniej stdin w pewnym momencie. („Argument - jest równoważny z -” w bash (1) jest niemożliwy do przeszukiwania)
bsb
5
Jakie jest dokładne znaczenie zakończenia „-” i gdzie jest to udokumentowane?
Zitrax
5
Uwaga : To nie cytuje argumentów (co jest ogólnie niebezpieczne). Tworzenie podpowłoki (z sh -c) również nie jest konieczne. Zobacz moją odpowiedź na alternatywę.
Tom Hale,
81

Poszukiwany alias to:

files = "!git diff --name-status \"$1\"^ \"$1\" #"

Z walidacją argumentów:

files = "!cd -- \"${GIT_PREFIX:-.}\" && [ x$# != x1 ] && echo commit-ish required >&2 || git diff --name-status \"$1\"^ \"$1\" #"

Końcowy# ważne - chroni wszystkie argumenty dostarczone przez użytkownika są przetwarzane z osłony (komentuje je).

Uwaga: gitumieszcza wszystkie argumenty dostarczone przez użytkownika na końcu wiersza poleceń. Aby zobaczyć to w akcji, spróbuj:GIT_TRACE=2 git files a b c d

W uciekły (ze względu na zagnieżdżanie) cytaty są ważne dla nazw zawierających spacje lub "; rm -rf --no-preserve-root /;)

Tom Hale
źródło
W najprostszych przypadkach jest to właściwa odpowiedź, naprawdę nie ma potrzeby komplikowania przez zawinięcie jej w funkcję lub sh-c.
Ed Randall,
4
Tak, !już sugeruje sh -c(pokazuje podczas przygotowywania GIT_TRACE=2), więc nie ma potrzeby uruchamiania kolejnej podpowłoki. Jakie problemy widzisz w bardziej skomplikowanych przypadkach?
Tom Hale,
Czy to działa, jeśli chcesz ustawić domyślne argumenty? np Chcę to zrobić, aby sprowadzić GitHub PR: fp = "! 1=${1:-$(git headBranch)}; 2=${2:-up}; git fetch -fu $2 pull/$1/head:$1; git checkout $1; git branch -u $2 #". Działa to świetnie bez dwóch pierwszych instrukcji, ale spada, jeśli ich użyjesz. (Ja headBranch = symbolic-ref --short HEADteż).
gib
2
Pracował go, to działa, jeśli ustawić nowe params, tak to jest w porządku: fp = "! a=${1:-$(git headBranch)}; b=${2:-up}; git fetch -fu $b pull/$a/head:$a; git checkout $a; git branch -u $b #".
gib
dlaczego "cytaty są wymagane?
Eugen Konkov
27

Użyj GIT_TRACE = 1 opisanego na stronie man git, aby przetwarzanie aliasu było przezroczyste:

$ git config alias.files
!git diff --name-status $1^ $1
$ GIT_TRACE=1 git files 1d49ec0
trace: exec: 'git-files' '1d49ec0'
trace: run_command: 'git-files' '1d49ec0'
trace: run_command: 'git diff --name-status $1^ $1' '1d49ec0'
trace: exec: '/bin/sh' '-c' 'git diff --name-status $1^ $1 "$@"' 'git diff --name-status $1^ $1' '1d49ec0'
trace: built-in: git 'diff' '--name-status' '1d49ec0^' '1d49ec0' '1d49ec0'
trace: run_command: 'less -R'
trace: exec: '/bin/sh' '-c' 'less -R' 'less -R'
MM      TODO

Twoje oryginalne polecenia działają z wersją git 1.8.3.4 (Eimantas zauważył, że zmieniło się to w 1.8.2.1).

Te sh -c '..' --i f() {..}; fopcje zarówno czysto obsłużyć „$ @” parametry w różny sposób (patrz z GIT_TRACE). Dołączenie „#” do aliasu pozwoliłoby również na parametry pozycyjne bez pozostawiania parametrów końcowych.

bsb
źródło
1
dziękuję za wyjaśnienia: te polecenia działają dla mnie na oryginalny problem, postępując zgodnie z twoją radą:files = "!git diff --name-status $1^ $1 #" files = "!git diff --name-status $1^"
user2291758
20

Jak stwierdził Drealmer powyżej :

" Bądź ostrożny, ! będzie działać w katalogu głównym repozytorium, więc używanie ścieżek względnych podczas wywoływania aliasu nie da oczekiwanych rezultatów. - Drealmer, 8 sierpnia 2013 o 16:28 »

GIT_PREFIX po ustawieniu git w podkatalogu, w którym się znajdujesz, możesz to obejść, najpierw zmieniając katalog:

git config --global alias.ls '! cd „$ {GIT_PREFIX: -.}”; ls -al '

Pierre-Olivier Vares
źródło
Mam z tym również problem (polecenia są uruchamiane w katalogu głównym repozytorium), ale wydaje się, że to rozwiązanie nic nie robi. (Jeśli to ważne, używam OS X.)
waldyrious
Ups ... alias git to pseudonim, który utworzyłem.
Pierre-Olivier Vares,
(od git 1.8.2) git config --set alias.alias = '! git config - globalny alias. 1 $ „2 $”
Pierre-Olivier Vares
Oto, co dla mnie cd ${GIT_PREFIX:-.} &&.
zadziałało
Zacytuj to. !cd "${GIT_PREFIX:-.}" && ls -al
mirabilos
8

Chciałem to zrobić z aliasem, który to robi:

git checkout $1;
git merge --ff-only $2;
git branch -d $2;

Na koniec utworzyłem skrypt powłoki o nazwie git-m, który ma następującą treść:

#!/bin/bash -x
set -e

#by naming this git-m and putting it in your PATH, git will be able to run it when you type "git m ..."

if [ "$#" -ne 2 ]
then
  echo "Wrong number of arguments. Should be 2, was $#";
  exit 1;
fi

git checkout $1;
git merge --ff-only $2;
git branch -d $2;

Ma to tę zaletę, że to dużo bardziej czytelne, ponieważ znajduje się na wielu liniach. Dodatkowo lubię móc nazywać bash za pomocą -xi set -e. Prawdopodobnie możesz zrobić to wszystko jako alias, ale byłoby to bardzo brzydkie i trudne do utrzymania.

Ponieważ plik ma nazwę git-m, możesz go uruchomić w następujący sposób:git m foo bar

Daniel Kaplan
źródło
1
Bardzo mi się to podoba, ale nie byłem w stanie dowiedzieć się, jak korzystać z autouzupełniania, którego chcę z tym podejściem. W przypadku aliasów możesz to zrobić: '!f() { : git branch ; ... }; f'i automatycznie uzupełni alias jako gałąź, która jest bardzo przydatna.
Hassek
Tak, myślę, że wolę, aby nie trywialne rzeczy były wykonywane jako pojedyncze pliki skryptów na ścieżce. Minusem jest jednak to, że tracisz automatyczne uzupełnianie takich rzeczy jak referencje. Możesz to naprawić, ręcznie konfigurując własne automatyczne uzupełnianie. Znowu jednak podoba mi się to, że możesz po prostu upuścić skrypt do folderu na ścieżce i zacznie on działać, ale w celu automatycznego uzupełniania musisz go „załadować”, więc zwykle znajduje się w moim .bashrcpliku, który źródłem. Ale nie sądzę, że zmieniam sposób, w jaki automatycznie uzupełniam argumenty w skrypt tak samo, jak sam skrypt, i będzie to możliwe tylko podczas tworzenia.
thecoshman
4

Wpadłem na coś podobnego; mam nadzieję, że mogę opublikować moje notatki. Jedna rzecz, która myli mnie w kwestii gitaliasów z argumentami, prawdopodobnie pochodzi z git help config(Mam wersję git 1.7.9.5):

Jeśli rozszerzenie aliasu jest poprzedzone wykrzyknikiem, będzie traktowane jak polecenie powłoki. Na przykład, zdefiniowanie „alias.new =! Gitk --all --not ORIG_HEAD”, wywołanie „git new” jest równoważne uruchomieniu polecenia powłoki „gitk --all --not ORIG_HEAD”. Zauważ, że polecenia powłoki będą wykonywane z katalogu najwyższego poziomu repozytorium, który niekoniecznie musi być bieżącym katalogiem. [...]

Sposób, w jaki go widzę - jeśli alias „będzie traktowany jako polecenie powłoki”, gdy będzie poprzedzony wykrzyknikiem - dlaczego miałbym używać funkcji lub sh -c argumentów; dlaczego po prostu nie napisać mojego polecenia takim, jakim jest?

Nadal nie znam odpowiedzi - ale myślę, że w rzeczywistości istnieje niewielka różnica w wynikach. Oto mały test - wrzuć to do swojego .git/configlub swojego ~/.gitconfig:

[alias]
  # ...
  ech = "! echo rem: "
  shech = "! sh -c 'echo rem:' "
  fech = "! f() { echo rem: ; }; f " # must have ; after echo!
  echargs = "! echo 0[[\"$0\"]] 1-\"$1\"/ A-"$@"/ "
  fechargs = "! f() { echo 0[[\"$0\"]] 1-\"$1\"/ A-"$@"/ ; }; f "

Oto, co otrzymuję z tych aliasów:

$ git ech word1 word2
rem: word1 word2

$ git shech word1 word2
rem:

$ git fech word1 word2
rem:

$ git echargs word1 word2
0[[ echo 0[["$0"]] 1-"$1"/ A-$@/ ]] 1-word1/ A-word1 word2/ word1 word2

$ git fechargs word1 word2
0[[ f() { echo 0[["$0"]] 1-"$1"/ A-$@/ ; }; f ]] 1-word1/ A-word1 word2/

... lub: gdy używasz polecenia „zwykłego” po aliasie !„jak jest” git- wówczas gitautomatycznie dołącza listę argumentów do tego polecenia! Sposobem na uniknięcie tego jest wywołanie skryptu jako funkcji - lub argumentush -c .

Inną interesującą rzeczą (dla mnie) jest to, że w skrypcie powłoki zwykle oczekuje się, że zmienna automatyczna $0będzie nazwą pliku skryptu. Ale w przypadku gitfunkcji aliasu $0argumentem jest zasadniczo treść całości łańcucha określającego to polecenie (zgodnie z wpisem w pliku konfiguracyjnym).

Dlatego, jak sądzę, jeśli zdarzy ci się źle napisać - w poniższym przypadku byłoby to unikanie podwójnych cudzysłowów zewnętrznych:

[alias]
  # ...
  fail = ! \"echo 'A' 'B'\"

... - wtedy gitnie powiedzie się (przynajmniej dla mnie) nieco tajemnicza wiadomość:

$ git fail
 "echo 'A' 'B'": 1: echo 'A' 'B': not found
fatal: While expanding alias 'fail': ' "echo 'A' 'B'"': No such file or directory

Myślę, że skoro git„widział” cały ciąg jako tylko jeden argument do !- próbował uruchomić go jako plik wykonywalny; i odpowiednio nie udało się znaleźć "echo 'A' 'B'"jako pliku.

W każdym razie, w kontekście git help configpowyższego cytatu, spekuluję, że bardziej dokładne jest stwierdzenie czegoś takiego: „ ... wywołanie„ git new ”jest równoważne uruchomieniu polecenia powłoki„ gitk --all - not ORIG_HEAD $ @ ”, gdzie $ @ to argumenty przekazywane do aliasu polecenia git z wiersza poleceń w czasie wykonywania. ”. Myślę, że to by również wyjaśniało, dlaczego „bezpośrednie” podejście w OP nie działa z parametrami pozycyjnymi.

sdaau
źródło
niezły test. Szybki sposób na sprawdzenie wszystkich możliwości!
albfan
failpróbuje uruchomić polecenie o nazwie „echo” A ”B” (tzn. długość 10 znaków). Ten sam błąd sh -c "'echo a b'"i ta sama przyczyna, zbyt wiele warstw cytatów
bsb