Git псевдонім з позиційними параметрами


261

В основному я намагаюся псевдонім:

git files 9fa3

... для виконання команди:

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

але, схоже, git не передає позиційні параметри команді alias. Я намагався:

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

... і кілька інших, але ті не спрацювали.

Виродженим випадком буде:

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

... як я можу зробити цю роботу?


17
Зауважте, що в git 1.8.2.1 це можна зробити без функції оболонки (ваш оригінальний підхід з $1повинен працювати).
Еймантас

7
@Eimantas Ви б хотіли детальніше відповісти? Це не працює для мене, і я не можу знайти жодної документації про це.
pavon

@Eimantas немає нічого про це в примітках до випуску .
Кну

1
я можу підтвердити, що я можу запускати команди оболонки з аргументами без будь-яких шенагіган в Git 2.11.
anarcat

Відповіді:


365

Найбільш очевидний спосіб - це використання функції оболонки:

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

Псевдонім без !трактується як команда Git; напр commit-all = commit -a.

З !, він запускається як власна команда в оболонці, дозволяючи вам використовувати більш сильну магію, як це.

UPD
Оскільки команди виконуються в корені сховища, ви можете використовувати ${GIT_PREFIX}змінну при посиланні на назви файлів у командах


8
Дякую, це виглядає абсолютно правильно: [alias] files = "! F () {echo $ 3 $ 2 $ 1;}; f"; $ git files abc => cba
user400575

1
@ KohányiRóbert: Це насправді не питання сценарію оболонки; це особливість git config. Псевдонім без !трактується як команда Git; напр commit-all = commit -a. З !, він запускається як власна команда в оболонці, дозволяючи вам використовувати більш сильну магію, як це.
Каскабель

40
Будьте уважні, !запустіть у корені сховища, тому використання відносних шляхів при виклику псевдоніма не дасть результатів, які ви могли очікувати.
Drealmer

4
@RobertDailey Це не порушує, він просто не реалізує. Див. Stackoverflow.com/questions/342969/…, як його додати.
Каскабель

3
Примітка . Це не цитує аргументів (що в цілому небезпечно). Також функція зайва. Дивіться мою відповідь для отримання додаткових пояснень.
Том Хейл

96

Ви також можете посилатися shбезпосередньо (замість створення функції):

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

(Зверніть увагу на тире в кінці рядка - вам це знадобиться.)


8
Якщо ви ділитесь командою, ви, мабуть, хочете використовувати sh, оскільки це сама по собі оболонка, і вона доступна в переважній більшості систем. Використання оболонки за замовчуванням працює, лише якщо команда працює так, як написано для всіх оболонок.
номотетіс

12
Я вважаю --за краще, -як це більш звично і менше шансів випадково мати на увазі stdin в якийсь момент. ("Аргумент - - еквівалентно -" в bash (1) неможливо змінити)
bsb


5
Яке точне значення закінчення "-" і де це документально зафіксовано?
Zitrax

5
Примітка . Це не цитує аргументів (що в цілому небезпечно). Створення підкольори (з sh -c) також непотрібне. Дивіться мою відповідь щодо альтернативи.
Том Хейл

81

Псевдонім, який ви шукаєте, це:

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

З підтвердженням аргументу:

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

Остаточне# важливо - це запобігає всі надані користувачем аргументи з обробки з допомогою оболонки (це коментарі їх).

Примітка: gitставить усі аргументи, що надаються користувачем, в кінці командного рядка. Щоб побачити це в дії, спробуйте:GIT_TRACE=2 git files a b c d

Утеклі (через вкладення) лапки важливі для імен файлів, що містять пробіли або "; rm -rf --no-preserve-root /;)


У найпростіших випадках це правильна відповідь, насправді немає необхідності ускладнювати, вкладаючи його у функцію або sh -c.
Ед Рендалл

4
Так, !вже мається на увазі sh -c(показано під час попередньої підготовки GIT_TRACE=2), тому немає необхідності запускати інший підзагін. Які проблеми ви бачите у складніших випадках?
Том Хейл

Чи працює це, якщо ви хочете встановити аргументи за замовчуванням? наприклад , я хочу зробити це , щоб принести 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 #". Це чудово працює без перших двох тверджень, але випадає, якщо ви їх використовуєте. (Я headBranch = symbolic-ref --short HEADтакож).
гиб

2
Опрацював це, він працює, якщо ви встановите нові парами, тож це добре : fp = "! a=${1:-$(git headBranch)}; b=${2:-up}; git fetch -fu $b pull/$a/head:$a; git checkout $a; git branch -u $b #".
gib

чому "потрібні котирування?
Євген Конков

27

Використовуйте GIT_TRACE = 1, описаний на сторінці git man, щоб зробити псевдонім обробкою прозорим:

$ 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

Ваші оригінальні команди працюють з версією git 1.8.3.4 (Еймантас зазначив, що це змінилося в 1.8.2.1).

В sh -c '..' --і f() {..}; fваріанти як чисто обробляти «$ @» параметри по - різному (див з GIT_TRACE). Додавання "#" до псевдоніма також дозволить позиційні параметри, не виходячи з кінцевих.


1
дякую за пояснення: ці команди працюють на мене над початковою проблемою, дотримуючись ваших порад:files = "!git diff --name-status $1^ $1 #" files = "!git diff --name-status $1^"
user2291758

20

Як зазначено вище, Drealmer :

" Будь обережний, ! запуститься в корені сховища, тому використання відносних шляхів при виклику псевдоніма не дасть результатів, які ви могли очікувати. - Drealmer 8 серпня 1313 о 16:28 »

GIT_PREFIX встановивши git в підкаталог, в якому ви перебуваєте, ви можете його обійти, спершу змінивши каталог:

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


У мене теж проблеми з цим (команди, які виконуються в корені сховища), але це рішення, здається, нічого не робить. (Якщо це має значення, я використовую OS X.)
waldyrious

На жаль ... git псевдонім - псевдонім, який я зробив.
П'єр-Олів'є Варес

(оскільки git 1.8.2) git config --set alias.alias = '! git config - глобальний псевдонім. $ 1 "$ 2" '
П'єр-Олів'є Варес

Це те , що в кінцевому підсумку працює для мене: «префікс для ваших GIT псевдонімів (тобто команди запуску оболонки і потребують правильного PWD) з cd ${GIT_PREFIX:-.} &&.» (джерело: stackoverflow.com/a/21929373/266309 )
waldyrious

Цитуйте це. !cd "${GIT_PREFIX:-.}" && ls -al
mirabilos

8

Я хотів це зробити з псевдонімом, який робить це:

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

Врешті-решт я створив скрипт оболонки на ім’я git-m, який містить цей вміст:

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

Це має перевагу в тому, що вона набагато розбірливіша, оскільки знаходиться в декількох рядках. Плюс мені подобається, що я можу викликати bash з -xі set -e. Ви, ймовірно, можете робити все це як псевдонім, але це було б дуже потворно і важко в обслуговуванні.

Оскільки файл названий, git-mви можете запустити його так:git m foo bar


1
Мені це подобається набагато більше, але я не зміг зрозуміти, як використовувати автозаповнення, яке я хочу при такому підході. Для псевдонімів ви можете це зробити: '!f() { : git branch ; ... }; f'і він автоматично заповнить псевдонім як гілка, яка дуже зручна.
Хассек

Так, я думаю, що я вважаю за краще робити нетривіальні речі як окремі файли сценаріїв на шляху. Знизу, хоча так, ви втрачаєте автоматичне завершення таких речей, як довідки. Однак ви можете це виправити, вручну налаштувавши власне автоматичне завершення. Знову ж таки, мені подобається, що ви можете просто запустити скрипт у папку на шляху, і він почне працювати, але для автоматичного завершення вам потрібно «завантажити» його, тому зазвичай це .bashrcфайл у моєму файлі. Але я не думаю, що я змінюю те, як я автоматично завершую аргументи до сценарію так само, як сам сценарій, і це було б лише під час розробника.
thecoshman

4

Просто наткнувся на щось подібне; сподіваюся, що все в порядку, щоб розмістити свої замітки. Одне, що бентежить мене щодо gitпсевдонімів з аргументами, ймовірно, походить з git help config(у мене версія git 1.7.9.5):

Якщо розширення псевдоніму встановлено префіксом оклику, воно буде розглядатися як команда оболонки. Наприклад, визначаючи "alias.new =! Gitk --all --not ORIG_HEAD", виклик "git new" еквівалентний виконанню команди оболонки "gitk --all - not ORIG_HEAD". Зауважте, що команди оболонки виконуватимуться з каталогу верхнього рівня репозиторію, який не обов'язково може бути поточним каталогом. [...]

Я бачу це - якщо псевдонім "буде розглядатися як команда оболонки", коли він буде встановлений префіксом оклику - чому мені потрібно використовувати функцію або sh -cаргументи; чому б просто не написати мою команду як є?

Я досі не знаю відповіді - але я думаю, що насправді є невелика різниця в результаті. Ось невеликий тест - киньте це у своє .git/configчи своє ~/.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 "

Ось що я запускаю ці псевдоніми:

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

... або: коли ви використовуєте команду "plain" після псевдоніма !"as-is" git- тоді gitавтоматично додається до цієї команди список аргументів! Спосіб уникнути цього - насправді називати свій скрипт або функцією, або як аргумент sh -c.

Ще одна цікава річ (для мене) полягає в тому, що в сценарії оболонки зазвичай очікується, що автоматична змінна $0буде іменем файлу сценарію. Але для gitфункції псевдоніму $0аргументом є, в основному, вміст всієї рядка, що вказує цю команду (як введено у конфігураційний файл).

Ось чому, я думаю, якщо у вас трапиться неправильне котирування - у нижченаведеному випадку це стане уникнутим зовнішніх подвійних лапок:

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

... - тоді gitне вдалося б (для мене, принаймні) дещо криптованого повідомлення:

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

Я думаю, оскільки git"бачив" цілий рядок лише одним аргументом !- він намагався запустити його як виконуваний файл; і, відповідно, не вдалося знайти "echo 'A' 'B'"як файл.

У будь-якому випадку, в контексті git help configцитати вище, я б припускав, що більш точно сказати щось на зразок: " ... виклик" git new "еквівалентно виконанню команди оболонки" gitk --all - not ORIG_HEAD $ @ ", де $ @ - аргументи, передані псевдоніму команди git з командного рядка під час виконання. ... ". Я думаю, що це також пояснить, чому "прямий" підхід в ОП не працює з позиційними параметрами.


приємний тест. Швидкий спосіб перевірити всі можливості!
albfan

failнамагається запустити команду під назвою "echo 'A' 'B" (тобто 10 символів завдовжки). Одна sh -c "'echo a b'"і та ж помилка з тієї ж причини, занадто багато шарів цитат
bsb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.