Alias ​​Git con parametri posizionali


261

Fondamentalmente sto provando ad alias:

git files 9fa3

... per eseguire il comando:

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

ma git non sembra passare parametri posizionali al comando alias. Ho provato:

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

... e pochi altri, ma quelli non hanno funzionato.

Il caso degenerato sarebbe:

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

... come posso farlo funzionare?


17
Nota che in git 1.8.2.1 è possibile farlo senza la funzione shell (il tuo approccio originale $1dovrebbe funzionare).
Eimantas,

7
@Eimantas Ti andrebbe di elaborare una risposta? Non funziona per me e non riesco a trovare alcuna documentazione a riguardo.
pavone,

@Eimantas non c'è nulla a riguardo nelle note di rilascio .
Knu,

1
posso confermare che posso eseguire comandi di shell con argomenti senza shenanigans in Git 2.11.
Anarcat,

Risposte:


365

Il modo più ovvio è usare una funzione shell:

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

Un alias senza !viene trattato come un comando Git; per esempiocommit-all = commit -a .

Con il !, viene eseguito come comando proprio nella shell, permettendoti di usare magie più forti come questa.

UPD
Poiché i comandi vengono eseguiti nella radice del repository, è possibile utilizzare la ${GIT_PREFIX}variabile quando si fa riferimento ai nomi dei file nei comandi


8
Grazie, questo sembra esattamente giusto: [alias] files = "! F () {echo $ 3 $ 2 $ 1;}; f"; $ git files abc => cba
user400575

1
@ KohányiRóbert: In realtà non è una domanda di script shell; questo è un particolare di git config. Un alias senza !viene trattato come un comando Git; es commit-all = commit -a. Con il !, viene eseguito come comando proprio nella shell, permettendoti di usare magie più forti come questa.
Cascabel,

40
Fai attenzione, !verrà eseguito alla radice del repository, quindi l'uso di percorsi relativi quando chiami il tuo alias non darà i risultati che potresti aspettarti.
Drealmer,

4
@RobertDailey Non lo rompe, semplicemente non lo implementa. Vedi stackoverflow.com/questions/342969/… per come aggiungerlo.
Cascabel,

3
Nota : questo non cita argomenti (che è pericoloso in generale). Inoltre, una funzione non è necessaria. Vedi la mia risposta per ulteriori spiegazioni.
Tom Hale,

96

Puoi anche fare riferimento shdirettamente (invece di creare una funzione):

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

(Nota il trattino alla fine della linea - ne avrai bisogno.)


8
Se condividi il comando, probabilmente vorrai utilizzarlo sh, poiché è di per sé una shell ed è disponibile nella stragrande maggioranza dei sistemi. L'uso della shell predefinita funziona solo se il comando funziona come scritto per tutte le shell.
Nomothetis,

12
Io preferisco --per -come è più familiare e meno probabilità di stdin accidentalmente media ad un certo punto. ("Un argomento di - equivale a -" in bash (1) è ingovernabile)
bsb


5
Qual è il significato esatto del finale '-' e dove è documentato?
Zitrax,

5
Nota : questo non cita argomenti (che è pericoloso in generale). Anche la creazione di una sotto-shell (con sh -c) non è necessaria. Vedi la mia risposta per un'alternativa.
Tom Hale,

81

L'alias che stai cercando è:

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

Con convalida argomento:

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

Il finale# è importante: impedisce a tutti gli argomenti forniti dall'utente di essere elaborati dalla shell (li commenta).

Nota: gitinserisce tutti gli argomenti forniti dall'utente alla fine della riga di comando. Per vederlo in azione, prova:GIT_TRACE=2 git files a b c d

Le virgolette di escape (dovute alla nidificazione) sono importanti per i nomi di file contenenti spazi o "; rm -rf --no-preserve-root /;)


Per i casi più semplici questa è la risposta giusta, non c'è davvero bisogno di complicarla avvolgendola in una funzione o sh -c.
Ed Randall,

4
Sì, !implica già sh -c(mostrato quando si antepone GIT_TRACE=2), quindi non è necessario eseguire un'altra sotto-shell. Quali problemi vedi in casi più complicati?
Tom Hale

Funziona se vuoi impostare argomenti predefiniti? ad esempio io voglio fare questo per andare a prendere un 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 #". Funziona alla grande senza le prime due affermazioni, ma cade se le usi. (Anche io headBranch = symbolic-ref --short HEAD).
Gib

2
Ha lavorato fuori, funziona se si imposta nuove params, quindi questo va bene: fp = "! a=${1:-$(git headBranch)}; b=${2:-up}; git fetch -fu $b pull/$a/head:$a; git checkout $a; git branch -u $b #".
Gib

perché "sono richieste le virgolette?
Eugen Konkov,

28

Usa GIT_TRACE = 1 descritto nella pagina man di git per rendere trasparente l'elaborazione dell'alias:

$ 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

I tuoi comandi originali funzionano con la versione 1.8.3.4 di git (Eimantas ha notato che questo è cambiato in 1.8.2.1).

Le opzioni sh -c '..' --e f() {..}; fgestiscono entrambe in modo pulito i parametri "$ @" in diversi modi (vedi con GIT_TRACE). L'aggiunta di "#" a un alias consentirebbe anche i parametri posizionali senza lasciare quelli finali.


1
grazie per le spiegazioni: quei comandi funzionano per me sul problema originale, seguendo il tuo consiglio:files = "!git diff --name-status $1^ $1 #" files = "!git diff --name-status $1^"
user2291758

20

Come affermato da Drealmer sopra :

" Stai attento, ! verrà eseguito alla radice del repository, quindi l'utilizzo di percorsi relativi quando si chiama il proprio alias non darà i risultati previsti. - Drealmer 8 agosto 13 alle 16:28 »

GIT_PREFIX essendo impostato da git nella sottodirectory in cui ti trovi, puoi aggirare questo cambiando prima la directory:

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


Sto riscontrando problemi anche con questo (comandi eseguiti alla radice del repository) ma questa soluzione non sembra fare nulla. (Se è importante, sto usando OS X.)
waldyrious

Oops ... git alias è un alias che ho creato.
Pierre-Olivier Vares,

(da git 1.8.2) git config --set alias.alias = '! git config - alias globale. $ 1 "$ 2" '
Pierre-Olivier Vares

Questo è ciò che alla fine ha funzionato per me: "aggiungi il prefisso ai tuoi alias git (che eseguono i comandi shell e hanno bisogno del giusto pwd) con cd ${GIT_PREFIX:-.} &&. " (fonte: stackoverflow.com/a/21929373/266309 )
waldyrious

Cita questo. !cd "${GIT_PREFIX:-.}" && ls -al
mirabilos,

8

Volevo farlo con un alias che fa questo:

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

Alla fine, ho creato uno script di shell chiamato git-m che ha questo contenuto:

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

Questo ha il vantaggio di essere molto più leggibile perché si trova su più righe. Inoltre mi piace poter chiamare bash con -xeset -e . Probabilmente puoi fare tutto questo come un alias, ma sarebbe molto brutto e difficile da mantenere.

Poiché il file è denominato git-mè possibile eseguirlo in questo modo:git m foo bar


1
Mi piace molto di più, ma non sono stato in grado di capire come utilizzare il completamento automatico che desidero con questo approccio. Sugli alias puoi farlo: '!f() { : git branch ; ... }; f'e completerà automaticamente l'alias come un ramo che è super utile.
Hassek,

Sì, penso che preferisco fare le cose non banali come singoli file di script sul percorso. Il lato negativo è sì, perdi il completamento automatico di cose come riferimenti. Puoi comunque risolverlo configurando manualmente il tuo completamento automatico. Ancora una volta, però, mi piace il fatto che puoi semplicemente rilasciare uno script in una cartella sul percorso e inizierà a funzionare, ma per il completamento automatico, devi "caricarlo", quindi di solito è nel mio .bashrcfile che sorgente. Ma non penso di cambiare il modo in cui completo automaticamente gli argomenti in uno script tanto quanto lo script stesso, e sarebbe solo durante lo sviluppo.
thecoshman,

4

Ho appena incontrato qualcosa di simile; spero sia bello pubblicare i miei appunti. Una cosa che mi confonde sugli gitalias con gli argomenti, probabilmente viene dalla git help config(ho la versione 1.7.9.5 di git):

Se l'espansione dell'alias è preceduta da un punto esclamativo, verrà trattata come un comando shell. Ad esempio, definendo "alias.new =! Gitk --all --not ORIG_HEAD", l'invocazione "git new" equivale all'esecuzione del comando shell "gitk --all --not ORIG_HEAD". Si noti che i comandi della shell verranno eseguiti dalla directory di livello superiore di un repository, che potrebbe non essere necessariamente la directory corrente. [...]

Per come la vedo io - se un alias "sarà trattato come un comando di shell" quando è preceduto da un punto esclamativo - perché dovrei usare una funzione, o sh -c con argomenti; perché non scrivere semplicemente il mio comando così com'è?

Ancora non conosco la risposta, ma in realtà penso che ci sia una leggera differenza nei risultati. Ecco un piccolo test: gettalo nel tuo .git/configo nel tuo ~/.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 "

Ecco cosa ottengo eseguendo questi alias:

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

... oppure: quando si utilizza un comando "semplice" dopo !"così com'è" in un gitalias, quindi gitsi aggiunge automaticamente l'elenco degli argomenti a quel comando! Un modo per evitarlo, in effetti, è chiamare il tuo script come una funzione o come argomento sh -c.

Un'altra cosa interessante qui (per me), è che in uno script shell, in genere ci si aspetta che la variabile automatica $0sia il nome file dello script. Ma per una gitfunzione alias, l' $0argomento è sostanzialmente il contenuto dell'intero stringa che specifica quel comando (come inserito nel file di configurazione).

Ecco perché, immagino, se ti capita di citare un errore - nel caso seguente, questo sfuggirebbe alle doppie virgolette esterne:

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

... - allora gitfallirebbe con (almeno per me) un messaggio un po 'criptico:

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

Penso, dal momento che git"visto" un'intera stringa come solo un argomento per !- ha provato a eseguirlo come un file eseguibile; e di conseguenza non è riuscito a trovare "echo 'A' 'B'"un file.

In ogni caso, nel contesto della git help configcitazione sopra, speculerei che è più preciso dichiarare qualcosa del tipo: " ... l'invocazione" git new "equivale all'esecuzione del comando shell" gitk --all --not ORIG_HEAD $ @ ", dove $ @ sono gli argomenti passati all'alias del comando git dalla riga di comando in fase di esecuzione. ... ". Penso che ciò spiegherebbe anche perché l'approccio "diretto" in OP non funziona con i parametri posizionali.


bel test. Un modo rapido per verificare tutte le possibilità!
albfan,

failsta cercando di eseguire un comando chiamato "echo 'A' 'B" (cioè lungo 10 caratteri). Stesso errore sh -c "'echo a b'"e stessa causa, troppi strati di virgolette
bsb
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.