Git-Alias ​​mit Positionsparametern

261

Grundsätzlich versuche ich einen Alias:

git files 9fa3

... um den Befehl auszuführen:

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

aber git scheint keine Positionsparameter an den Alias-Befehl zu übergeben. Ich habe versucht:

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

... und ein paar andere, aber die haben nicht funktioniert.

Der entartete Fall wäre:

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

... wie kann ich das zum Laufen bringen?

user400575
quelle
17
Beachten Sie, dass es in Git 1.8.2.1 möglich ist, dies ohne Shell-Funktion zu tun (Ihr ursprünglicher Ansatz mit $1sollte funktionieren).
Eimantas
7
@Eimantas Möchten Sie eine Antwort ausarbeiten? Es funktioniert bei mir nicht und ich kann keine Dokumentation darüber finden.
Pavon
@Eimantas gibt es nichts darüber in den Release Notes though.
Knu
1
Ich kann bestätigen, dass ich Shell-Befehle mit Argumenten ohne Spielereien in Git 2.11 ausführen kann.
Anarcat

Antworten:

365

Der naheliegendste Weg ist die Verwendung einer Shell-Funktion:

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

Ein Alias ​​ohne ! wird als Git-Befehl behandelt. zB commit-all = commit -a.

Mit dem ! wird es als eigener Befehl in der Shell ausgeführt, sodass Sie so stärkere Magie einsetzen können.

UPD
Da Befehle im Stammverzeichnis des Repositorys ausgeführt werden, können Sie ${GIT_PREFIX}Variablen verwenden, wenn Sie auf die Dateinamen in Befehlen verweisen

Cascabel
quelle
8
Danke, das sieht genau richtig aus: [alias] files = "! F () {echo $ 3 $ 2 $ 1;}; f"; $ git files abc => cba
user400575
1
@ KohányiRóbert: Das ist eigentlich keine Shell-Skript-Frage; Das ist eine Besonderheit von Git Config. Ein Alias ​​ohne !wird als Git-Befehl behandelt. zB commit-all = commit -a. Mit dem !wird es als eigener Befehl in der Shell ausgeführt, sodass Sie so stärkere Magie einsetzen können.
Cascabel
40
Seien Sie vorsichtig, !wird im Stammverzeichnis des Repositorys ausgeführt. Wenn Sie also beim Aufrufen Ihres Alias ​​relative Pfade verwenden, erhalten Sie nicht die erwarteten Ergebnisse.
Drealmer
4
@RobertDailey Es bricht es nicht, es implementiert es einfach nicht. Siehe stackoverflow.com/questions/342969/... , wie es hinzuzufügen.
Cascabel
3
Hinweis : Hier werden keine Argumente zitiert (was im Allgemeinen gefährlich ist). Auch eine Funktion ist nicht erforderlich. Weitere Erklärungen finden Sie in meiner Antwort .
Tom Hale
96

Sie können auch shdirekt referenzieren (anstatt eine Funktion zu erstellen):

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

(Beachten Sie den Bindestrich am Ende der Zeile - das brauchen Sie.)

Mipadi
quelle
8
Wenn Sie den Befehl freigeben, möchten Sie ihn wahrscheinlich verwenden sh, da dies an sich eine Shell ist und auf den meisten Systemen verfügbar ist. Die Verwendung der Standard-Shell funktioniert nur, wenn der Befehl für alle Shells wie geschrieben funktioniert.
Nomothetis
12
Ich bevorzuge es --, -da es vertrauter ist und weniger wahrscheinlich ist, dass es irgendwann versehentlich stdin bedeutet. ("Ein Argument von - ist gleichbedeutend mit -" in Bash (1) ist unauslöschlich)
bsb
5
Was ist die genaue Bedeutung der Endung '-' und wo ist sie dokumentiert?
Zitrax
5
Hinweis : Hier werden keine Argumente zitiert (was im Allgemeinen gefährlich ist). Das Erstellen einer Sub-Shell (mit sh -c) ist ebenfalls nicht erforderlich. Siehe meine Antwort für eine Alternative.
Tom Hale
81

Der gesuchte Alias ​​lautet:

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

Mit Argumentvalidierung:

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

Das Finale# ist wichtig - es verhindert, dass alle vom Benutzer angegebenen Argumente von der Shell verarbeitet werden (es kommentiert sie aus).

Hinweis: gitPlatziert alle vom Benutzer angegebenen Argumente am Ende der Befehlszeile. Um dies in Aktion zu sehen, versuchen Sie:GIT_TRACE=2 git files a b c d

Die maskierten Anführungszeichen (aufgrund von Verschachtelungen) sind wichtig für Dateinamen, die Leerzeichen enthalten, oder "; rm -rf --no-preserve-root /;)

Tom Hale
quelle
Für die einfachsten Fälle ist dies die richtige Antwort. Es besteht keine Notwendigkeit, dies zu komplizieren, indem Sie es in eine Funktion oder sh-c einschließen.
Ed Randall
4
Ja, !impliziert sh -cdies bereits (wird beim Voranstellen angezeigt GIT_TRACE=2), sodass keine weitere Unter-Shell ausgeführt werden muss. Welche Probleme sehen Sie in komplizierteren Fällen?
Tom Hale
Funktioniert dies, wenn Sie Standardargumente festlegen möchten? zB möchte ich dies tun, um einen Github PR zu holen : fp = "! 1=${1:-$(git headBranch)}; 2=${2:-up}; git fetch -fu $2 pull/$1/head:$1; git checkout $1; git branch -u $2 #". Dies funktioniert ohne die ersten beiden Anweisungen hervorragend, fällt jedoch herunter, wenn Sie sie verwenden. (Ich habe headBranch = symbolic-ref --short HEADauch).
Gib
2
Es hat geklappt, es funktioniert, wenn Sie neue Parameter festlegen, also ist dies in Ordnung : fp = "! a=${1:-$(git headBranch)}; b=${2:-up}; git fetch -fu $b pull/$a/head:$a; git checkout $a; git branch -u $b #".
Gib
Warum sind "Anführungszeichen erforderlich?
Eugen Konkov
27

Verwenden Sie GIT_TRACE = 1, das auf der Git-Manpage beschrieben ist, um die Alias-Verarbeitung transparent zu machen:

$ 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

Ihre ursprünglichen Befehle funktionieren mit der Git-Version 1.8.3.4 (Eimantas hat festgestellt, dass sich dies in 1.8.2.1 geändert hat).

Die Optionen sh -c '..' --und f() {..}; fbehandeln die "$ @" -Parameter auf unterschiedliche Weise sauber (siehe mit GIT_TRACE). Das Anhängen von "#" an einen Alias ​​würde auch Positionsparameter zulassen, ohne die nachfolgenden zu belassen.

bsb
quelle
1
files = "!git diff --name-status $1^ $1 #" files = "!git diff --name-status $1^"
Vielen
20

Wie von Drealmer oben angegeben :

" Achtung, ! wird im Stammverzeichnis des Repositorys ausgeführt. Wenn Sie also beim Aufrufen Ihres Alias ​​relative Pfade verwenden, erhalten Sie nicht die erwarteten Ergebnisse. - Drealmer 8. August 13 um 16:28 »

GIT_PREFIX Wenn Sie von git auf das Unterverzeichnis gesetzt werden, in dem Sie sich befinden, können Sie dies umgehen, indem Sie zuerst das Verzeichnis ändern:

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

Pierre-Olivier Vares
quelle
Ich habe auch Probleme damit (Befehle werden im Stammverzeichnis des Repositorys ausgeführt), aber diese Lösung scheint nichts zu bewirken. (Wenn es darauf ankommt, verwende ich OS X.)
Waldyrious
Ups ... Git Alias ​​ist ein Alias, den ich gemacht habe.
Pierre-Olivier Vares
(seit git 1.8.2) git config --set alias.alias = '! git config --global alias. $ 1 "$ 2" '
Pierre-Olivier Vares
Dies ist, was für mich letztendlich funktioniert hat: "Stellen Sie Ihren Git-Aliasnamen (die Shell-Befehle ausführen und das richtige pwd benötigen) cd ${GIT_PREFIX:-.} &&." (Quelle: stackoverflow.com/a/21929373/266309 )
waldyrious
Zitieren Sie dies. !cd "${GIT_PREFIX:-.}" && ls -al
Mirabilos
8

Ich wollte dies mit einem Alias ​​tun, der dies tut:

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

Am Ende habe ich ein Shell-Skript namens git-m erstellt , das diesen Inhalt hat:

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

Dies hat den Vorteil, dass es viel besser lesbar ist, da es sich um mehrere Zeilen handelt. Außerdem mag ich es, mit -xund bash anrufen zu könnenset -e . Sie können das Ganze wahrscheinlich als Alias ​​machen, aber es wäre super hässlich und schwer zu pflegen.

Da die Datei benannt ist git-m, können Sie sie folgendermaßen ausführen:git m foo bar

Daniel Kaplan
quelle
1
Ich mag das auch viel mehr, aber ich konnte nicht herausfinden, wie ich die gewünschte automatische Vervollständigung mit diesem Ansatz verwenden soll. Bei Aliasnamen können Sie dies tun: '!f() { : git branch ; ... }; f'Der Alias ​​wird automatisch als Zweig vervollständigt, was sehr praktisch ist.
Hassek
Ja, ich glaube, ich bevorzuge es, die nicht trivialen Dinge als einzelne Skriptdateien auf dem Pfad ausführen zu lassen. Der Nachteil ist jedoch, dass Sie die automatische Vervollständigung von Dingen wie Referenzen verlieren. Sie können dies jedoch beheben, indem Sie Ihre eigene automatische Vervollständigung manuell konfigurieren. Auch hier gefällt mir, dass Sie ein Skript einfach in einen Ordner auf dem Pfad ablegen können und es funktioniert, aber für die automatische Vervollständigung müssen Sie es "laden", sodass es normalerweise in meiner .bashrcDatei ist, die ich als Quelle habe. Aber ich glaube nicht, dass ich die Art und Weise, wie ich Argumente für ein Skript automatisch vervollständige, so sehr ändere wie das Skript selbst, und das nur während der Entwicklung.
Thecoshman
4

Ich bin gerade auf etwas Ähnliches gestoßen; Ich hoffe, es ist OK, meine Notizen zu posten. Eine Sache, die mich über gitAliase mit Argumenten verwirrt , kommt wahrscheinlich von git help config(ich habe Git-Version 1.7.9.5):

Wenn der Alias-Erweiterung ein Ausrufezeichen vorangestellt wird, wird sie als Shell-Befehl behandelt. Wenn Sie beispielsweise "alias.new =! Gitit --all --not ORIG_HEAD" definieren, entspricht der Aufruf "git new" dem Ausführen des Shell-Befehls "gitk --all --not ORIG_HEAD". Beachten Sie, dass Shell-Befehle aus dem Verzeichnis der obersten Ebene eines Repositorys ausgeführt werden, das nicht unbedingt das aktuelle Verzeichnis sein muss. [...]

So wie ich es sehe - wenn ein Alias ​​"als Shell-Befehl behandelt wird", wenn ein Ausrufezeichen vorangestellt wird - warum sollte ich eine Funktion oder sh -cArgumente verwenden müssen? Warum schreibst du meinen Befehl nicht einfach so wie er ist?

Ich weiß die Antwort immer noch nicht - aber ich denke tatsächlich, dass es einen kleinen Unterschied im Ergebnis gibt. Hier ist ein kleiner Test - werfen Sie diesen in Ihren .git/configoder Ihren ~/.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 "

Folgendes bekomme ich, um diese Aliase auszuführen:

$ 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/

... oder: Wenn Sie einen "einfachen" Befehl nach dem !"wie besehen " in einem gitAlias gitverwenden, wird die Argumentliste automatisch an diesen Befehl angehängt! Eine Möglichkeit, dies zu vermeiden, besteht darin, Ihr Skript entweder als Funktion oder als Argument für aufzurufen sh -c.

Eine andere interessante Sache hier (für mich) ist, dass man in einem Shell-Skript normalerweise erwartet, dass die automatische Variable $0der Dateiname des Skripts ist. Bei einer gitAlias-Funktion ist das $0Argument im Grunde genommen der Inhalt der gesamten Zeichenfolge, die diesen Befehl angibt (wie in der Konfigurationsdatei eingegeben).

Aus diesem Grund, denke ich, wenn Sie zufällig falsch zitieren - im folgenden Fall würde dies den äußeren doppelten Anführungszeichen entgehen:

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

... - dann gitwürde mit (zumindest für mich) etwas kryptischer Nachricht scheitern:

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

Ich denke, da giteine ganze Zeichenfolge nur als ein Argument "gesehen" wurde !, wurde versucht, sie als ausführbare Datei auszuführen. und dementsprechend konnte es nicht "echo 'A' 'B'"als Datei gefunden werden.

In jedem Fall git help configwürde ich im Zusammenhang mit dem obigen Zitat spekulieren, dass es genauer ist, Folgendes anzugeben : " ... der Aufruf" git new "entspricht dem Ausführen des Shell-Befehls" gitk --all --not ORIG_HEAD " $ @ ", wobei $ @ die Argumente sind, die zur Laufzeit von der Befehlszeile an den git-Befehlsalias übergeben werden. ... ". Ich denke, das würde auch erklären, warum der "direkte" Ansatz in OP nicht mit Positionsparametern funktioniert.

sdaau
quelle
schöner Test. Ein schneller Weg, um alle Möglichkeiten zu prüfen!
Albfan
failversucht, einen Befehl namens "echo 'A' 'B" (dh 10 Zeichen lang) auszuführen. Gleicher Fehler sh -c "'echo a b'"und gleiche Ursache, zu viele Anführungszeichen
bsb