Truy xuất nhật ký cam kết cho một dòng cụ thể trong một tệp?


503

Có cách nào để git cung cấp cho bạn nhật ký cam kết cho các cam kết đã chạm vào một dòng cụ thể trong một tệp không?

Giống như git blame, nhưng git blamesẽ cho bạn thấy cam kết LAST đã chạm vào một dòng cụ thể.

Tôi thực sự muốn nhận được một nhật ký tương tự, không phải là danh sách các cam kết ở bất kỳ đâu trong tệp, mà chỉ là các cam kết chạm vào một dòng cụ thể.


Câu trả lời:


632

Xem thêm Git: khám phá những cam kết từng chạm vào một loạt các dòng .


Kể từ Git 1.8.4 , git log-Lđể xem sự phát triển của một loạt các dòng.

Ví dụ: giả sử bạn nhìn vào git blameđầu ra của. Ở đây -L 150,+11có nghĩa là "chỉ nhìn vào các dòng 150 đến 150 + 11":

$ git blame -L 150,+11 -- git-web--browse.sh
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &

Và bạn muốn biết lịch sử của dòng 155.

Sau đó, sử dụng git log. Ở đây, -L 155,155:git-web--browse.shcó nghĩa là "theo dõi sự phát triển của các dòng 155 đến 155 trong tệp có tên git-web--browse.sh".

$ git log --pretty=short -u -L 155,155:git-web--browse.sh
commit 81f42f11496b9117273939c98d270af273c8a463
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: support opera, seamonkey and elinks

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -143,1 +143,1 @@
-firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)

commit a180055a47c6793eaaba6289f623cff32644215b
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: coding style

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -142,1 +142,1 @@
-    firefox|iceweasel)
+firefox|iceweasel)

commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
Author: Christian Couder <chriscool@tuxfamily.org>

    Rename 'git-help--browse.sh' to 'git-web--browse.sh'.

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- /dev/null
+++ b/git-web--browse.sh
@@ -0,0 +127,1 @@
+    firefox|iceweasel)

2
'git log --topo-order --graph -u -L 155,155: git-web--browse.sh' - điều này đã gây ra một lỗi nghiêm trọng: 'tên đối tượng không hợp lệ 155,155'. Phiên bản Git: 1.8.3.2. Bất kỳ đề xuất?
BairDev

12
Nâng cấp lên Git 1.8.4 trở lên.
Matt McClure

4
điều gì xảy ra nếu tôi muốn biết "lịch sử của dòng 155 trong commitA" (thay vì dòng 155 trong mục ĐẦU). Tôi chỉ có thể sử dụng git log commitA-hash -L 155,155:file-name?
Ida

@Flimm, tôi không có sở thích mạnh mẽ.
Matt McClure

điều này hoạt động tốt, ngoại trừ nếu tệp đã được di chuyển / đổi tên ... có vẻ như --follow không muốn được kết hợp với các đối số phạm vi dòng.
Mike Ellery

66

Bạn có thể nhận được một tập hợp các cam kết bằng cách sử dụng pick-ax.

git log -S'the line from your file' -- path/to/your/file.txt

Điều này sẽ cung cấp cho bạn tất cả các cam kết ảnh hưởng đến văn bản đó trong tệp đó. Nếu tệp được đổi tên tại một số điểm, bạn có thể thêm --follow-Parent.

Nếu bạn muốn kiểm tra các cam kết tại mỗi lần chỉnh sửa này, bạn có thể dẫn kết quả đó đến git show:

git log ... | xargs -n 1 git show

5
Tôi không chắc chắn tôi thấy làm thế nào điều này giúp. Nếu văn bản bị ảnh hưởng, thì dòng đó không giống nhau nữa, vì vậy, pickaxe sẽ chỉ cho bạn thấy sự thay đổi gần đây nhất. Sau đó, bạn phải làm git log -S'the previous version of the line'và cứ thế, chính xác như thể bạn sẽ làm với git blame -L. Và nó sẽ chậm hơn git blame, vì nó phải tìm văn bản ở mọi nơi, không chỉ ở nơi nhất định.
Cascabel

Bạn có thể sử dụng một regex trong đó để làm cho nó dễ chấp nhận hơn. Hiện tại không có cách nào để "điều hướng qua các bản vá" ngược thời gian mà không có một số kịch bản phức tạp. Tôi hy vọng rằng gitk sẽ có được chức năng này trong chế độ xem bản vá trong tương lai.
Adam Dymitruk

3
Tại sao điều này được gọi là rìu ? :)
Ciprian Tomoiagă


14

Một cách cực kỳ dễ dàng để làm điều này là sử dụng vim-fugitive . Chỉ cần mở tệp trong vim, chọn (các) dòng bạn quan tâm sử dụng V, sau đó nhập

:Glog

Bây giờ bạn có thể sử dụng :cnext:cprevđể xem tất cả các phiên bản của tệp nơi dòng đó được sửa đổi. Tại bất kỳ thời điểm nào, nhập :Gblameđể xem thông tin sha, tác giả và ngày.


13

Đơn giản hóa câu trả lời của @ matt -

git blame -L14,15 -- <file_path>

Ở đây bạn sẽ nhận được một lỗi cho một dòng 14 to 15.

-Ltùy chọn mong đợi Rangenhư một thông số, chúng tôi không thể có được một Blamedòng sử dụng -Ltùy chọn này .

Tài liệu tham khảo


10

Tôi không tin có bất cứ điều gì tích hợp cho việc này. Thật khó hiểu bởi thực tế là hiếm khi một dòng thay đổi nhiều lần mà phần còn lại của tệp cũng thay đổi đáng kể, vì vậy bạn sẽ có xu hướng kết thúc với số dòng thay đổi rất nhiều.

Nếu bạn đủ may mắn rằng dòng luôn có một số đặc điểm nhận dạng, ví dụ: gán cho một biến có tên không bao giờ thay đổi, bạn có thể sử dụng lựa chọn regex cho git blame -L. Ví dụ:

git blame -L '/variable_name *= */',+1

Nhưng điều này chỉ tìm thấy kết quả khớp đầu tiên cho regex đó, vì vậy nếu bạn không có cách kết hợp tốt với dòng này, thì nó không quá hữu ích.

Bạn có thể hack một cái gì đó lên, tôi cho rằng. Tôi không có thời gian để viết mã ngay bây giờ, nhưng ... một cái gì đó dọc theo những dòng này. Chạy đi git blame -n -L $n,$n $file. Trường đầu tiên là cam kết trước đó được chạm vào và trường thứ hai là số dòng trong cam kết đó , vì nó có thể đã thay đổi. Lấy chúng và chạy git blame -n $n,$n $commit^ $file, tức là điều tương tự bắt đầu từ cam kết trước lần thay đổi tệp cuối cùng.

(Lưu ý rằng điều này sẽ làm bạn thất bại nếu lần xác nhận cuối cùng thay đổi dòng là một cam kết hợp nhất. Cách chính này có thể xảy ra nếu dòng được thay đổi như là một phần của giải quyết xung đột hợp nhất.)

Chỉnh sửa: Tôi đã xảy ra trên bài đăng danh sách gửi thư này từ tháng 3 năm 2011, trong đó đề cập đến điều đó tiggit guicó một tính năng sẽ giúp bạn làm điều này. Có vẻ như tính năng này đã được xem xét, nhưng chưa hoàn thành, đối với chính git.


6

Điều này sẽ gọi git blamecho tất cả các phiên bản có ý nghĩa để show line $LINEcủa tập tin $FILE:

git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE

Như thường lệ, lỗi đổ lỗi cho thấy số sửa đổi ở đầu mỗi dòng. Bạn có thể chắp thêm

| sort | uniq -c

để có được kết quả tổng hợp, một cái gì đó giống như một danh sách các cam kết đã thay đổi dòng này. (Không hoàn toàn, nếu mã chỉ được di chuyển xung quanh, điều này có thể hiển thị cùng một ID cam kết hai lần cho các nội dung khác nhau của dòng. Để phân tích chi tiết hơn, bạn phải thực hiện so sánh git blamekết quả cho các cam kết liền kề. )


Một lần nữa tôi nghĩ rằng điều này không hoạt động, bởi vì nó không theo dõi vị trí trước đó của dòng đó. Vì vậy, nếu một dòng được thêm 2 lần xác nhận trước đó, bạn sẽ xem xét một dòng khác
Vic Seedoubleyew

6

Đây là một giải pháp xác định bí danh git, vì vậy bạn sẽ có thể sử dụng nó như thế:

git rblame -M -n -L '/REGEX/,+1' FILE

Ví dụ đầu ra:

00000000 18 (Not Committed Yet 2013-08-19 13:04:52 +0000 728) fooREGEXbar
15227b97 18 (User1 2013-07-11 18:51:26 +0000 728) fooREGEX
1748695d 23 (User2 2013-03-19 21:09:09 +0000 741) REGEXbar

Bạn có thể xác định bí danh trong .gitconfig hoặc chỉ cần chạy lệnh sau

git config alias.rblame !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); echo $line; done' dumb_param

Đây là một lớp lót xấu xí, vì vậy đây là một hàm bash tương đương bị che khuất:

git-rblame () {
    local commit line
    while line=$(git blame "$@" $commit 2>/dev/null); do
        commit="${line:0:8}^"
        if [ "00000000^" == "$commit" ]; then
            commit=$(git rev-parse HEAD)
        fi
        echo $line
    done
}

Giải pháp pickaxe ( git log --pickaxe-regex -S'REGEX ' ) sẽ chỉ cung cấp cho bạn các bổ sung / xóa dòng, không phải là các thay đổi khác của dòng chứa biểu thức chính quy.

Một hạn chế của giải pháp này là git đổ lỗi chỉ trả về trận đấu REGEX thứ nhất, vì vậy nếu có nhiều trận đấu tồn tại, đệ quy có thể "nhảy" để đi theo một dòng khác. Hãy chắc chắn kiểm tra đầu ra lịch sử đầy đủ để phát hiện ra những "bước nhảy" đó và sau đó sửa REGEX của bạn để bỏ qua các dòng ký sinh trùng.

Cuối cùng, đây là một phiên bản thay thế chạy git show trên mỗi cam kết để có được sự khác biệt đầy đủ:

git config alias.rblameshow !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); git show $commit; done' dumb_param

2

Bạn có thể trộn git blamegit logcác lệnh để lấy bản tóm tắt của từng cam kết trong lệnh git blame và nối chúng lại. Một cái gì đó giống như tập lệnh bash + awk sau đây. Nó nối các bản tóm tắt cam kết dưới dạng mã bình luận nội tuyến.

git blame FILE_NAME | awk -F" " \
'{
   commit = substr($0, 0, 8);
   if (!a[commit]) {
     query = "git log --oneline -n 1 " commit " --";
     (query | getline a[commit]);
   }
   print $0 "  // " substr(a[commit], 9);
 }'

Trong một dòng:

git blame FILE_NAME | awk -F" " '{ commit = substr($0, 0, 8); if (!a[commit]) { query = "git log --oneline -n 1 " commit " --"; (query | getline a[commit]); } print $0 "  // " substr(a[commit], 9); }'

2

Trong trường hợp của tôi, số dòng đã thay đổi rất nhiều theo thời gian. Tôi cũng đã sử dụng git 1.8.3, không hỗ trợ regex trong "git đổ lỗi -L". (RHEL7 vẫn có 1.8.3)

myfile=haproxy.cfg
git rev-list HEAD -- $myfile | while read i
do
    git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"
done | grep "<sometext>"

Lót:

myfile=<myfile> ; git rev-list HEAD -- $myfile | while read i; do     git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"; done | grep "<sometext>"

Điều này tất nhiên có thể được thực hiện thành một kịch bản hoặc một chức năng.

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.