Git alias พร้อมพารามิเตอร์ตำแหน่ง


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ควรใช้งานได้)
Eimantas

7
@Eimantas คุณสนใจที่จะอธิบายรายละเอียดอย่างละเอียดหรือไม่? มันใช้งานไม่ได้สำหรับฉันและฉันไม่พบเอกสารใด ๆ เกี่ยวกับเรื่องนี้
pavon

@Eimantas ไม่มีอะไรเกี่ยวกับเรื่องนี้ในบันทึกประจำรุ่นว่า
Knu

1
ฉันสามารถยืนยันได้ว่าฉันสามารถเรียกใช้คำสั่งเชลล์พร้อมอาร์กิวเมนต์ได้โดยไม่ต้องมี shenanigans ใน 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 โดยเฉพาะ นามแฝงที่ไม่มี!จะถือว่าเป็นคำสั่ง Git commit-all = commit -aเช่น ด้วย!, มันทำงานเป็นคำสั่งของมันเองในเชลล์, ให้คุณใช้เวทย์มนตร์ที่แข็งแกร่งกว่านี้
Cascabel

40
ระวัง!จะทำงานที่รูทของที่เก็บดังนั้นการใช้พา ธ สัมพัทธ์เมื่อเรียกนามแฝงของคุณจะไม่ให้ผลลัพธ์ที่คุณคาดหวัง
Drealmer

4
@RobertDailey มันไม่ได้ทำลายมันก็ไม่ได้ใช้มัน ดูstackoverflow.com/questions/342969/…สำหรับวิธีเพิ่ม
Cascabel

3
หมายเหตุ : นี่ไม่ได้อ้างถึงการขัดแย้ง (ซึ่งเป็นอันตรายโดยทั่วไป) นอกจากนี้ฟังก์ชั่นไม่จำเป็น ดูคำตอบของฉันสำหรับคำอธิบายเพิ่มเติม
Tom Hale

96

คุณสามารถอ้างอิงshโดยตรง (แทนที่จะสร้างฟังก์ชั่น):

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

(สังเกตเส้นประที่ส่วนท้ายของบรรทัดคุณจำเป็นต้องใช้)


8
หากคุณกำลังแบ่งปันคำสั่งคุณอาจต้องการใช้shเพราะมันเป็นเชลล์เองและมันมีอยู่ในระบบส่วนใหญ่ การใช้เชลล์เริ่มต้นจะทำงานเฉพาะเมื่อคำสั่งทำงานตามที่เขียนไว้สำหรับเชลล์ทั้งหมด
Nomothetis

12
ฉันชอบที่--จะ-คุ้นเคยมากกว่าและมีโอกาสน้อยกว่าที่จะหมายถึง stdin ในบางจุด ( "การทะเลาะกันของ - เทียบเท่ากับ -" ในทุบตี (1) เป็น ungoogleable)
BSB

3
ดูเพิ่มเติมอย่างเป็นทางการ Git วิกิพีเดีย - นามแฝงขั้นสูงที่มีข้อโต้แย้ง

5
ความหมายที่แท้จริงของตอนจบ '-' คืออะไรและจัดทำเอกสารไว้ที่ใด
Zitrax

5
หมายเหตุ : นี่ไม่ได้อ้างถึงการขัดแย้ง (ซึ่งเป็นอันตรายโดยทั่วไป) การสร้าง sub-shell (with sh -c) ก็ไม่จำเป็นเช่นกัน ดูคำตอบของฉันสำหรับทางเลือก
Tom Hale

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
Ed Randall

4
ใช่!แสดงอยู่แล้วsh -c(แสดงเมื่อเริ่มต้นGIT_TRACE=2) ดังนั้นจึงไม่จำเป็นต้องเรียกใช้ sub-shell อื่น คุณเห็นปัญหาอะไรในกรณีที่ซับซ้อนมากขึ้น
Tom Hale

ใช้งานได้ไหมถ้าคุณต้องการตั้งค่าอาร์กิวเมนต์เริ่มต้น เช่นผมต้องการที่จะทำเช่นนี้เพื่อเรียก Github fp = "! 1=${1:-$(git headBranch)}; 2=${2:-up}; git fetch -fu $2 pull/$1/head:$1; git checkout $1; git branch -u $2 #"PR: มันใช้งานได้ดีโดยไม่ต้องมีสองข้อความแรก แต่จะล้มลงถ้าคุณใช้มัน (ฉันมีheadBranch = symbolic-ref --short HEADเช่นกัน)
gib

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

ทำไม"ต้องมีการเสนอราคา?
Eugen Konkov

28

ใช้ GIT_TRACE = 1 ที่อธิบายไว้ในหน้า man git เพื่อทำให้การประมวลผลสมนามโปร่งใส:

$ 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 (Eimantas สังเกตว่าการเปลี่ยนแปลงนี้ใน 1.8.2.1)

sh -c '..' --และ f() {..}; fตัวเลือกทั้งสองอย่างหมดจดจัดการ "$ @" พารามิเตอร์ในรูปแบบที่แตกต่างกัน (เห็นกับ GIT_TRACE) การต่อท้าย "#" กับนามแฝงจะช่วยให้พารามิเตอร์ตำแหน่งโดยไม่ต้องออกต่อท้าย


1
ขอบคุณสำหรับคำอธิบาย: คำสั่งเหล่านั้นทำงานให้ฉันกับปัญหาเดิมตามคำแนะนำของคุณ:files = "!git diff --name-status $1^ $1 #" files = "!git diff --name-status $1^"
2291758

20

ตามที่ระบุโดย Drealmer ด้านบน :

" ระวัง, ! จะทำงานที่รูทของที่เก็บดังนั้นการใช้พา ธ สัมพัทธ์เมื่อเรียกนามแฝงของคุณจะไม่ให้ผลลัพธ์ที่คุณคาดหวัง - Drealmer 8 ส.ค. 56 เวลา 16:28 »

GIT_PREFIX ถูกกำหนดโดย git ไปยังไดเรกทอรีย่อยที่คุณอยู่คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยการเปลี่ยนไดเรกทอรีก่อน:

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


ฉันมีปัญหากับสิ่งนี้เช่นกัน (คำสั่งถูกเรียกใช้ที่รูทของที่เก็บ) แต่วิธีนี้ดูเหมือนจะไม่ทำอะไรเลย (ถ้าเป็นเรื่องสำคัญฉันกำลังใช้ OS X)
วาลดีริยา

โอ๊ะโอชื่อคอมไพล์เป็นชื่อแทนที่ฉันทำ
Pierre-Olivier Vares

(ตั้งแต่ git 1.8.2) git config --set alias.alias = '! git config - นามแฝงทั่วโลก $ 1 "$ 2" '
Pierre-Olivier Vares

นี่คือสิ่งที่จบลงด้วยการทำงานสำหรับฉัน: "คำนำหน้านามแฝง 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;

นี้มีผลประโยชน์ที่มันเป็นมากชัดเจนมากขึ้นเพราะมันอยู่ในหลายบรรทัด บวกฉันเช่นความสามารถในการเรียกทุบตีด้วยและ-x set -eคุณอาจทำสิ่งนี้ทั้งหมดเป็นนามแฝง แต่มันน่าเกลียดและยากที่จะรักษา

เนื่องจากชื่อไฟล์git-mคุณสามารถเรียกใช้ดังนี้:git m foo bar


1
ฉันชอบสิ่งนี้มากขึ้นเช่นกัน แต่ฉันไม่สามารถหาวิธีใช้การเติมข้อความอัตโนมัติที่ฉันต้องการด้วยวิธีการนี้ได้ ในนามแฝงคุณสามารถทำได้: '!f() { : git branch ; ... }; f'และมันจะเติมชื่อแทนโดยอัตโนมัติให้เป็นสาขาซึ่งมีประโยชน์มาก
Hassek

ใช่ฉันคิดว่าฉันชอบการทำสิ่งที่ไม่สำคัญเหมือนไฟล์สคริปต์แต่ละรายการบนพา ธ ข้อเสียคือใช่คุณทำสิ่งต่าง ๆ ให้สมบูรณ์โดยอัตโนมัติเช่นการอ้างอิง คุณสามารถแก้ไขได้ด้วยการกำหนดค่าการเติมข้อมูลอัตโนมัติของคุณเอง อีกครั้งแม้ว่าฉันชอบที่คุณสามารถวางสคริปต์ลงในโฟลเดอร์บนเส้นทางและมันจะเริ่มทำงาน แต่สำหรับการทำให้สมบูรณ์อัตโนมัติคุณต้อง 'โหลด' มันดังนั้นโดยปกติจะอยู่ใน.bashrcไฟล์ของฉันที่ฉันมา แต่ฉันไม่คิดว่าฉันจะเปลี่ยนวิธีที่ฉันทำข้อโต้แย้งอัตโนมัติให้เป็นสคริปต์ได้มากเท่าที่ตัวสคริปต์เองและมันจะเกิดขึ้นเฉพาะในช่วง dev
thecoshman

4

เพิ่งชนเข้ากับสิ่งที่คล้ายกัน; หวังว่าจะเป็น oK เพื่อโพสต์บันทึกของฉัน สิ่งหนึ่งที่ทำให้ฉันสับสนเกี่ยวกับgitนามแฝงที่มีข้อโต้แย้งอาจมาจากgit help config(ฉันมีรุ่นคอมไพล์ 1.7.9.5):

หากส่วนขยาย alias ถูกเติมหน้าด้วยเครื่องหมายอัศเจรีย์จะถูกใช้เป็นคำสั่งเชลล์ ตัวอย่างเช่นการกำหนด "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 จากบรรทัดคำสั่งตอนรันไทม์ ... " ฉันคิดว่านั่นจะอธิบายได้ว่าทำไมวิธี "โดยตรง" ใน OP ไม่ทำงานกับพารามิเตอร์ตำแหน่ง


การทดสอบที่ดี วิธีที่รวดเร็วในการตรวจสอบความเป็นไปได้ทั้งหมด!
albfan

failพยายามเรียกใช้คำสั่งที่เรียกว่า "echo 'A' 'B" (เช่นยาว 10 ตัวอักษร) ข้อผิดพลาดเดียวกันจากsh -c "'echo a b'"สาเหตุเดียวกันทำให้มีเลเยอร์คำพูดมากเกินไป
bsb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.