Làm cho 'git log' bỏ qua các thay đổi cho một số đường dẫn nhất định


121

Làm cách nào tôi có thể thực hiện git logchỉ hiển thị các cam kết đã thay đổi các tệp khác với những tệp tôi chỉ định?

Với git log, tôi có thể lọc các cam kết mà tôi thấy cho những cam kết chạm vào một tập hợp đường dẫn nhất định. Những gì tôi muốn là đảo ngược bộ lọc đó để chỉ các cam kết chạm vào các đường dẫn khác với những đường được chỉ định mới được liệt kê.

Tôi có thể có được những gì tôi muốn với

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

ở đâu 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";
    }
}

ngoại trừ tôi muốn một cái gì đó thanh lịch hơn thế.

Lưu ý rằng tôi không hỏi làm thế nào để làm cho git bỏ qua các tệp. Các tệp này phải được theo dõi và cam kết. Chỉ là, phần lớn thời gian, tôi không có hứng thú nhìn thấy chúng.

Câu hỏi liên quan: Làm thế nào để đảo ngược `git log --grep = <pattern>` hoặc Cách hiển thị nhật ký git không khớp với một mẫu Câu hỏi tương tự ngoại trừ các thông báo cam kết chứ không phải là đường dẫn.

Thảo luận trên diễn đàn về chủ đề này từ năm 2008: Re: Loại trừ các tệp khỏi git-diff Việc này trông có vẻ hứa hẹn nhưng chủ đề dường như đã cạn kiệt.


Tôi không chắc liệu có cách nào được tích hợp sẵn hay không và giải pháp perl của bạn trông khá ổn. Nếu bạn sửa đổi nó để chấp nhận các đường dẫn dưới dạng đối số dòng lệnh, bạn có thể chỉ cần tạo một bí danh gì đó giống như !f() { git log ... | path/to/filter-log.pl "$@" | git log --stdin --no-walk; f, hoặc thậm chí bọc phần đường ống đó vào tập lệnh.
Cascabel

Để giải quyết vấn đề, tôi sử dụng findđể lọc ra các thư mục có cam kết mà tôi không muốn thấy. Nếu tôi muốn bỏ qua các mục nhập nhật ký từ các cam kết được thực hiện đối với thư mục cấp gốc SiteConfigthì tôi sẽ nói:git log `find . -type d -mindepth 1 -maxdepth 1 ! -name *SiteConfig`
Noah Sussman

Đối với Git 1.9 / 2.0 (Q1 2014), xem câu trả lời của tôi dưới đây : git log --oneline --format=%s -- . ":!sub"sẽ làm việc (với ma thuật pathspec :(exclude)và hình thức ngắn của nó:! )
VonC

Câu trả lời:


214

Nó được triển khai ngay bây giờ (git 1.9 / 2.0, Q1 2014) với phép thuật pathspec:(exclude):! giới thiệu và dạng rút gọn của nó trong commit ef79b1fcommit 1649612 , bởi Nguyễn Thái Ngọc Duy ( pclouds) , có thể tìm thấy tài liệu tại đây .

Giờ đây, bạn có thể ghi lại mọi thứ ngoại trừ nội dung thư mục con:

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

Hoặc bạn có thể loại trừ các phần tử cụ thể trong thư mục con đó

  • một tệp cụ thể:

      git log -- . ":(exclude)sub/sub/file"
      git log -- . ":!sub/sub/file"
  • bất kỳ tệp nhất định nào trong sub:

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

Bạn có thể không phân biệt chữ hoa chữ thường loại trừ!

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

Như Kenny Evitt đã lưu ý

Nếu bạn đang chạy Git trong Bash shell, hãy sử dụng ':!sub'hoặc ":\!sub"thay thế để tránh bash: ... event not foundlỗi


Lưu ý: Git 2.13 (Quý 2 năm 2017) sẽ thêm một từ đồng nghĩa ^với!

Xem cam kết 859b7f1 , cam kết 42ebeb9 (08/02/2017) bởi Linus Torvalds ( torvalds) .
(Hợp nhất bởi Junio ​​C Hamano - gitster- in commit 015fba3 , 27 Feb 2017)

phép thuật pathspec: thêm ' ^' làm bí danh cho ' !'

Việc lựa chọn ' !' cho một pathspec phủ định không chỉ không phù hợp với những gì chúng ta làm cho các bản sửa đổi mà nó còn là một đặc điểm kinh khủng cho việc mở rộng shell vì nó cần trích dẫn.

Vì vậy, hãy thêm ' ^' làm bí danh thay thế cho mục nhập loại trừ pathspec.


Lưu ý rằng, trước Git 2.28 (Q3 2020), việc sử dụng pathspec phủ định, trong khi thu thập các đường dẫn bao gồm cả các đường dẫn chưa được theo dõi trong cây làm việc, đã bị hỏng.

Xem cam kết f1f061e (05/06/2020) của Elijah Newren ( newren) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết 64efa11 , ngày 18 tháng 6 năm 2020)

dir: sửa chữa xử lý các pathspec bị phủ định

Người trình bày: John Millikin
Người ký tên: Elijah Newren

do_match_pathspec()bắt đầu cuộc sống như match_pathspec_depth_1()và cho sự đúng đắn chỉ được cho là được gọi từ match_pathspec_depth(). match_pathspec_depth()sau đó được đổi tên thành match_pathspec(), vì vậy điều bất biến mà chúng ta mong đợi ngày nay là do_match_pathspec()không có người gọi trực tiếp bên ngoài match_pathspec().

Thật không may, ý định này đã bị mất với việc đổi tên của hai hàm và các lệnh gọi bổ sung do_match_pathspec()được thêm vào trong các cam kết 75a6315f74 (" ls-files: thêm đối sánh pathspec cho các mô-đun con", 2016-10-07, Git v2.11.0-rc0 - hợp nhất được liệt kê trong batch # 11 ) và 89a1f4aaf7 (" dir: nếu pathspec của chúng tôi có thể khớp với các tệp dưới một dir, hãy đệ quy vào nó", 2019-09-17, Git v2.24.0-rc0).

Tất nhiên, do_match_pathspec()có một lợi thế quan trọng hơn match_pathspec()- match_pathspec()sẽ mã hóa cờ thành một trong hai giá trị và những người gọi mới này cần phải chuyển một số giá trị khác cho cờ.

Ngoài ra, mặc dù việc gọi do_match_pathspec()trực tiếp là không chính xác, có thể không có bất kỳ sự khác biệt nào trong kết quả đầu ra có thể quan sát được, bởi vì lỗi chỉ có nghĩa là nó fill_diretory()sẽ tái diễn vào các thư mục không cần thiết.

Vì các lần kiểm tra đối sánh đường dẫn này với các đường dẫn riêng lẻ trong thư mục sẽ khiến các đường dẫn phụ đó bị lọc ra, sự khác biệt duy nhất so với việc sử dụng sai hàm là tính toán không cần thiết.

Lần thứ hai trong số những cuộc gọi xấu đó do_match_pathspec()đã tham gia - thông qua chuyển động trực tiếp hoặc thông qua sao chép + chỉnh sửa - vào một số cơ cấu tái cấu trúc sau này.

Xem cam kết 777b420347 (" dir: đồng bộ hóa treat_leading_path()read_directory_recursive()", 2019-12-19, Git v2.25.0-rc0 - hợp nhất ), 8d92fb2927 (" dir: thay thế thuật toán hàm mũ bằng thuật toán tuyến tính", 2020-04-01, Git v2.27.0 -rc0 - hợp nhất được liệt kê trong lô # 5 ) và 95c11ecc73 ("Khắc phục fill_directory()API dễ xảy ra lỗi ; làm cho nó chỉ trả về các kết quả phù hợp", 2020-04-01, Git v2.27.0-rc0 - hợp nhất được liệt kê trong lô # 5 ) .

Cuối cùng trong số đó đã giới thiệu cách sử dụng do_match_pathspec()trên một tệp riêng lẻ, và do đó dẫn đến các đường dẫn riêng lẻ được trả về mà không nên.

Vấn đề với việc gọi do_match_pathspec()thay vì match_pathspec()là bất kỳ mẫu phủ định nào như '':! Wish_path '' sẽ bị bỏ qua .

Thêm một match_pathspec_with_flags()chức năng mới để đáp ứng nhu cầu chỉ định các cờ đặc biệt trong khi vẫn kiểm tra chính xác các mẫu bị phủ định, thêm nhận xét lớn ở trên do_match_pathspec()để ngăn người khác sử dụng sai và sửa những người gọi hiện tại do_match_pathspec()để thay vào đó sử dụng match_pathspec()hoặc match_pathspec_with_flags().

Một lưu ý cuối cùng là DO_MATCH_LEADING_PATHSPECcần đặc biệt cân nhắc khi làm việc với DO_MATCH_EXCLUDE.

Vấn đề DO_MATCH_LEADING_PATHSPEClà nếu chúng ta có một pathspec như

*/Makefile

và chúng tôi đang kiểm tra một đường dẫn thư mục như

src/module/component

mà chúng tôi muốn coi nó là một kết quả phù hợp để chúng tôi đệ quy vào thư mục vì nó _might_ có một tệp có tên Makefileở đâu đó bên dưới.

Tuy nhiên, khi chúng ta đang sử dụng một mẫu loại trừ, tức là chúng ta có một pathspec như

:(exclude)*/Makefile

chúng tôi KHÔNG muốn nói rằng một đường dẫn thư mục như

src/module/component

là một trận đấu (phủ định).

Mặc dù có thể có một tệp có tên 'Makefile' ở đâu đó bên dưới thư mục đó, cũng có thể có các tệp khác và chúng tôi không thể loại trừ trước tất cả các tệp trong thư mục đó; chúng ta cần đệ quy và sau đó kiểm tra các tệp riêng lẻ.

Điều chỉnh DO_MATCH_LEADING_PATHSPEClogic để chỉ được kích hoạt cho các pathspec tích cực.


7
Bạn có thể làm nhiều tệp không?
Justin Thomas

12
@JustinThomas Tôi tin rằng (chưa được thử nghiệm) rằng bạn có thể lặp lại mẫu loại trừ đường dẫn đó nhiều lần ":(exclude)pathPattern1" ":(exclude)pathPattern2", do đó bỏ qua nhiều thư mục / tệp.
VonC

7
Nếu bạn đang chạy Git trong Bash shell, hãy sử dụng ':!sub'thay thế để tránh bash: ... event not foundlỗi . ":\!sub"không hoạt động.
Kenny Evitt

1
@KennyEvitt Cảm ơn bạn đã chỉnh sửa và nhận xét. Tôi đã bao gồm cái sau trong câu trả lời để dễ nhìn hơn.
VonC

2
Đối với những người tự hỏi tài liệu chính thức về chức năng này ở đâu, hãy xem git help glossary(mà tôi tìm thấy được liệt kê trong git help -g[mà tôi tìm thấy được đề xuất trong git help]).
ravron

4

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

Nếu đang sử dụng Bash, bạn sẽ có thể sử dụng tính năng lấp lánh mở rộng để chỉ nhận các tệp bạn cần:

$ 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

Bạn có thể kết hợp điều này với globstarhành động đệ quy.


7
Điều này không hiển thị các cam kết ảnh hưởng đến các tệp không còn tồn tại. Rất gần và tốt đẹp hack tất cả như nhau.
Anonymoose

-2

Bạn có thể tạm thời bỏ qua các thay đổi trong tệp với:

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

Trong tương lai, tất cả thay đổi các tập tin sẽ bị bỏ qua bởi git status, git commit -avv Khi bạn đã sẵn sàng để thực hiện những tác phẩm, chỉ cần đảo ngược nó:

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

và cam kết như bình thường.


9
Điều này dường như giải quyết một tình huống hơi khác. git update-index --skip-worktreekhông gây git loglọc các cam kết đã được thực hiện.
Anonymoose
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.