การทำให้ 'git log' ละเว้นการเปลี่ยนแปลงสำหรับบางเส้นทาง


121

ฉันจะgit logแสดงเฉพาะคอมมิตที่เปลี่ยนแปลงไฟล์อื่นนอกเหนือจากที่ฉันระบุได้อย่างไร

ด้วยgit logฉันสามารถกรองการคอมมิตที่ฉันเห็นกับผู้ที่สัมผัสชุดของเส้นทางที่กำหนด สิ่งที่ฉันต้องการคือการสลับตัวกรองนั้นเพื่อให้แสดงเฉพาะเส้นทางการสัมผัสที่นอกเหนือจากที่ระบุเท่านั้น

ฉันจะได้รับสิ่งที่ฉันต้องการด้วย

git log --format="%n/%n%H" --name-only | ~/filter-log.pl | git log --stdin --no-walk

อยู่ที่ไหนfilter-log.pl:

#!/usr/bin/perl
use strict;
use warnings;

$/ = "\n/\n";
<>;

while (<>) {
    my ($commit, @files) = split /\n/, $_;

    if (grep { $_ && $_ !~ m[^(/$|.etckeeper$|lvm/(archive|backup)/)] } @files) {
        print "$commit\n";
    }
}

ยกเว้นฉันต้องการบางสิ่งที่ค่อนข้างหรูหรากว่านั้น

โปรดทราบว่าฉันไม่ได้ถามวิธีทำให้คอมไพล์ละเว้นไฟล์ ไฟล์เหล่านี้ควรได้รับการติดตามและยืนยัน ก็แค่นั้นส่วนใหญ่ฉันไม่สนใจที่จะเห็นพวกเขา

คำถามที่เกี่ยวข้อง: วิธีการแปลง `git log --grep = <pattern>` หรือวิธีแสดงบันทึก git ที่ไม่ตรงกับรูปแบบคำถามนี้เป็นคำถามเดียวกันยกเว้นข้อความคอมมิตแทนที่จะเป็นพา ธ

การสนทนาในฟอรัมในหัวข้อนี้ตั้งแต่ปี 2008: Re: การแยกไฟล์จาก git-diffสิ่งนี้ดูดี แต่ดูเหมือนว่าเธรดจะแห้งไปแล้ว


ฉันไม่แน่ใจว่ามีวิธีในตัวหรือไม่และโซลูชัน perl ของคุณดูดีทีเดียว หากคุณแก้ไขเพื่อยอมรับเส้นทางเป็นอาร์กิวเมนต์บรรทัดคำสั่งคุณสามารถสร้างนามแฝงบางอย่างเช่น!f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; fหรือแม้แต่รวมส่วนของไปป์ไลน์นั้นเข้ากับสคริปต์ก็ได้เช่นกัน
Cascabel

เป็นวิธีแก้ปัญหาชั่วคราวฉันใช้findเพื่อกรองไดเรกทอรีที่ฉันไม่ต้องการดู ถ้าฉันต้องการละเว้นรายการบันทึกจากการคอมมิตที่ทำกับไดเร็กทอรีระดับรูทSiteConfigฉันจะพูดว่า:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman

สำหรับ Git 1.9 / 2.0 (Q1 2014) ดูคำตอบของฉันด้านล่าง : git log --oneline --format=%s -- . ":!sub"จะใช้งานได้ (ด้วยเวทย์มนตร์ pathspec :(exclude)และรูปแบบสั้น ๆ:! )
VonC

คำตอบ:


214

จะดำเนินการในขณะนี้ (คอมไพล์ 1.9 / 2.0 ไตรมาสที่ 1 ปี 2014) ด้วยการแนะนำมายากล pathspec :(exclude)และรูปแบบสั้น ๆ:!ในการกระทำ ef79b1fและกระทำ 1649612โดย NguyễnTháiNgọc Duy ( pclouds)เอกสารที่สามารถพบได้ที่นี่

ตอนนี้คุณสามารถบันทึกทุกอย่างยกเว้นเนื้อหาโฟลเดอร์ย่อย:

git log -- . ":(exclude)sub"
git log -- . ":!sub"

หรือคุณสามารถยกเว้นองค์ประกอบเฉพาะภายในโฟลเดอร์ย่อยนั้นได้

  • ไฟล์เฉพาะ:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • ไฟล์ใดก็ได้ภายในsub:

      git log -- . ":(exclude)sub/*file"
      git log -- . ":!sub/*file"
      git log -- . ":(exclude,glob)sub/*/file"

คุณสามารถทำให้กรณีการยกเว้นนั้นไม่คำนึงถึงได้!

git log -- . ":(exclude,icase)SUB"

ดังที่Kenny Evitt กล่าวไว้

หากคุณกำลังเรียกใช้ Git ใน Bash shell ให้ใช้':!sub'หรือ":\!sub"แทนเพื่อหลีกเลี่ยงbash: ... event not foundข้อผิดพลาด


หมายเหตุ: Git 2.13 (Q2 2017) จะเพิ่มคำพ้องความหมาย^ให้!

ดูกระทำ 859b7f1 , กระทำ 42ebeb9 (8 กุมภาพันธ์ 2017) โดยLinus Torvalds (torvalds )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 015fba3 , 27 กุมภาพันธ์ 2017)

pathspec magic: เพิ่ม ' ^' เป็นนามแฝงสำหรับ ' !'

การเลือก ' !' สำหรับ pathspec เชิงลบไม่เพียง แต่ไม่ตรงกับสิ่งที่เราทำเพื่อการแก้ไข แต่ยังเป็นตัวละครที่น่ากลัวสำหรับการขยายเชลล์เนื่องจากต้องมีการอ้างอิง

ดังนั้นให้เพิ่ม ' ^' เป็นนามแฝงอื่นสำหรับรายการ pathspec ที่ไม่รวม


โปรดทราบว่าก่อน Git 2.28 (Q3 2020) การใช้เส้นทางเชิงลบในขณะที่รวบรวมเส้นทางรวมถึงเส้นทางที่ไม่ได้ติดตามในแผนผังการทำงานนั้นใช้งานไม่ได้

ดูกระทำ f1f061e (5 มิถุนายน 2020) โดยเอลียาห์ Newren (newren )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 64efa11 , 18 มิถุนายน 2020)

dir: แก้ไขการรักษา pathspecs ที่เป็นลบ

รายงานโดย: John Millikin
ลงนามโดย: Elijah Newren

do_match_pathspec()เริ่มต้นชีวิตmatch_pathspec_depth_1()และเพื่อความถูกต้องควรถูกเรียกจากmatch_pathspec_depth()เท่านั้น match_pathspec_depth()ต่อมาได้เปลี่ยนชื่อเป็นmatch_pathspec()เพื่อให้คงที่ที่เราคาดหวังในวันนี้ก็คือว่าไม่เคยมีใครโทรติดต่อโดยตรงนอกdo_match_pathspec()match_pathspec()

น่าเสียดายที่ความตั้งใจนี้สูญหายไปพร้อมกับการเปลี่ยนชื่อของทั้งสองฟังก์ชันและมีการเพิ่มการเรียกdo_match_pathspec()เพิ่มเติมในคอมมิต 75a6315f74 (" ls-files: add pathspec matching for submodules", 2016-10-07, Git v2.11.0-rc0 - รวมอยู่ในbatch # 11 ) และ89a1f4aaf7 (" dir: ถ้า pathspec ของเราอาจตรงกับไฟล์ภายใต้ dir ให้เรียกคืน", 2019-09-17, Git v2.24.0-rc0)

แน่นอนว่าdo_match_pathspec()มีข้อได้เปรียบที่สำคัญมากกว่าmatch_pathspec()- match_pathspec()จะฮาร์ดโค้ดแฟล็กเป็นค่าใดค่าหนึ่งจากสองค่าและผู้โทรใหม่เหล่านี้จำเป็นต้องส่งต่อค่าอื่น ๆ สำหรับแฟล็ก

นอกจากนี้แม้ว่าการโทรdo_match_pathspec()โดยตรงจะไม่ถูกต้อง แต่ก็ไม่มีความแตกต่างใด ๆ ในผลลัพธ์สุดท้ายที่สังเกตได้เนื่องจากข้อผิดพลาดนั้นหมายความว่าfill_diretory()จะถูกเรียกคืนในไดเรกทอรีที่ไม่จำเป็น

เนื่องจากการตรวจสอบการจับคู่แบบไม่ตรงตามเส้นทางในแต่ละเส้นทางภายใต้ไดเร็กทอรีจะทำให้เส้นทางพิเศษเหล่านั้นถูกกรองออกความแตกต่างเพียงอย่างเดียวจากการใช้ฟังก์ชันที่ไม่ถูกต้องคือการคำนวณที่ไม่จำเป็น

การโทรที่ไม่ดีครั้งที่สองนั้นdo_match_pathspec()เกี่ยวข้อง - ผ่านการเคลื่อนไหวโดยตรงหรือผ่านการคัดลอก + แก้ไขไปยัง refactors ในภายหลัง

ดูคอมมิต 777b420347 (" dir: ซิงโครไนซ์treat_leading_path()และread_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - ผสาน ), 8d92fb2927 (" dir: แทนที่อัลกอริทึมเอกซ์โพเนนเชียลด้วย linear one", 2020-04-01, Git v2.27.0 -rc0 - รวมอยู่ในbatch # 5 ) และ95c11ecc73 ("Fix error-prone fill_directory()API; make it only return match", 2020-04-01, Git v2.27.0-rc0 - merge listed in batch # 5 ) .

รายการสุดท้ายที่แนะนำการใช้งานdo_match_pathspec()ในแต่ละไฟล์และส่งผลให้แต่ละเส้นทางถูกส่งกลับที่ไม่ควรเป็น

ปัญหาเกี่ยวกับการเรียกdo_match_pathspec()แทนmatch_pathspec()คือว่ารูปแบบใด ๆ เมื่อตะกี้เช่น '`:! unwanted_path`` จะถูกละเว้น

เพิ่มใหม่match_pathspec_with_flags()ฟังก์ชั่นเพื่อตอบสนองความต้องการของการระบุธงพิเศษในขณะที่ยังคงรูปแบบการตรวจสอบอย่างถูกต้องเมื่อตะกี้เพิ่มความคิดเห็นขนาดใหญ่ดังกล่าวข้างต้นdo_match_pathspec()เพื่อป้องกันไม่ให้ผู้อื่นจาก misusing มันและสายที่ปัจจุบันถูกต้องของdo_match_pathspec()แทนที่จะใช้อย่างใดอย่างหนึ่งหรือmatch_pathspec()match_pathspec_with_flags()

หนึ่งโน้ตตัวสุดท้ายก็คือความต้องการการพิจารณาเป็นพิเศษเมื่อทำงานกับDO_MATCH_LEADING_PATHSPECDO_MATCH_EXCLUDE

ประเด็นDO_MATCH_LEADING_PATHSPECคือถ้าเรามี pathspec เช่น

*/Makefile

และเรากำลังตรวจสอบเส้นทางไดเรกทอรีเช่น

src/module/component

ที่เราต้องการพิจารณาว่ามันตรงกันเพื่อที่เราจะนำกลับเข้าไปในไดเร็กทอรีเพราะ_might_ มีไฟล์ชื่อที่Makefileด้านล่าง

อย่างไรก็ตามเมื่อเราใช้รูปแบบการยกเว้นกล่าวคือเรามี pathspec เช่น

:(exclude)*/Makefile

เราไม่ต้องการบอกว่าเส้นทางไดเรกทอรีเช่น

src/module/component

เป็นการจับคู่ (เชิงลบ)

แม้ว่าอาจมีไฟล์ชื่อ 'Makefile' อยู่ที่ด้านล่างไดเร็กทอรีนั้น แต่ก็อาจมีไฟล์อื่น ๆ อยู่และเราไม่สามารถควบคุมไฟล์ทั้งหมดในไดเร็กทอรีนั้นล่วงหน้าได้ เราจำเป็นต้องเรียกคืนจากนั้นตรวจสอบไฟล์แต่ละไฟล์

ปรับDO_MATCH_LEADING_PATHSPECตรรกะเพื่อเปิดใช้งานเฉพาะสำหรับเส้นทางบวกเท่านั้น


7
คุณสามารถทำหลายไฟล์ได้หรือไม่?
Justin Thomas

12
@JustinThomas ฉันเชื่อว่า (ยังไม่ได้ทดสอบ) ว่าคุณสามารถทำซ้ำรูปแบบการยกเว้นเส้นทางนั้นได้หลายครั้ง":(exclude)pathPattern1" ":(exclude)pathPattern2"ดังนั้นจึงไม่สนใจหลายโฟลเดอร์ / ไฟล์
VonC

7
หากคุณกำลังใช้ Git ในเปลือกทุบตีใช้':!sub'แทนเพื่อหลีกเลี่ยงbash: ... event not foundข้อผิดพลาด ":\!sub"ไม่ทำงาน
Kenny Evitt

1
@KennyEvitt ขอบคุณสำหรับการแก้ไขและแสดงความคิดเห็น ฉันได้รวมข้อหลังไว้ในคำตอบเพื่อให้มองเห็นได้ชัดเจนขึ้น
VonC

2
สำหรับผู้ที่สงสัยว่าเอกสารอย่างเป็นทางการเกี่ยวกับฟังก์ชันนี้อยู่ที่ใดโปรดดูgit help glossary(ซึ่งฉันพบในรายการgit help -g[ที่ฉันพบว่าแนะนำในgit help])
ravron

4

TL; DR: shopt -s extglob && git log !(unwanted/glob|another/unwanted/glob)

หากคุณใช้Bashคุณควรจะสามารถใช้คุณสมบัติglobbing แบบขยายเพื่อรับเฉพาะไฟล์ที่คุณต้องการ:

$ cd -- "$(mktemp --directory)" 
$ git init
Initialized empty Git repository in /tmp/tmp.cJm8k38G9y/.git/
$ mkdir aye bee
$ echo foo > aye/foo
$ git add aye/foo
$ git commit -m "First commit"
[master (root-commit) 46a028c] First commit
 0 files changed
 create mode 100644 aye/foo
$ echo foo > bee/foo
$ git add bee/foo
$ git commit -m "Second commit"
[master 30b3af2] Second commit
 1 file changed, 1 insertion(+)
 create mode 100644 bee/foo
$ shopt -s extglob
$ git log !(bee)
commit ec660acdb38ee288a9e771a2685fe3389bed01dd
Author: My Name <jdoe@example.org>
Date:   Wed Jun 5 10:58:45 2013 +0200

    First commit

คุณสามารถรวมสิ่งนี้เข้ากับglobstarการดำเนินการแบบวนซ้ำได้


7
นี่ไม่ได้แสดงการคอมมิตที่มีผลต่อไฟล์ที่ไม่มีอยู่แล้ว แฮ็คใกล้มากและดีเหมือนกัน
Anonymoose

-2

คุณสามารถละเว้นการเปลี่ยนแปลงในไฟล์ได้ชั่วคราวด้วย:

git update-index --skip-worktree path/to/file

ก้าวไปข้างหน้าการเปลี่ยนแปลงทั้งหมดในไฟล์เหล่านั้นจะได้รับการปฏิเสธโดยgit status, git commit -aฯลฯ เมื่อคุณพร้อมที่จะกระทำไฟล์เหล่านั้นเพียงแค่ย้อนกลับมัน

git update-index --no-skip-worktree path/to/file

และกระทำตามปกติ


9
ดูเหมือนว่าจะกล่าวถึงสถานการณ์ที่แตกต่างกันเล็กน้อย git update-index --skip-worktreeไม่ก่อให้เกิดgit logการกรองคอมมิตที่ทำไปแล้ว
Anonymoose
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.