Git - làm cách nào để xem lịch sử thay đổi của một phương thức / hàm?


91

Vì vậy, tôi đã tìm thấy câu hỏi về cách xem lịch sử thay đổi của một tệp, nhưng lịch sử thay đổi của tệp cụ thể này rất lớn và tôi thực sự chỉ quan tâm đến những thay đổi của một phương pháp cụ thể. Vì vậy, liệu có thể xem lịch sử thay đổi chỉ cho phương pháp cụ thể đó không?

Tôi biết điều này sẽ yêu cầu git phân tích mã và phân tích sẽ khác nhau đối với các ngôn ngữ khác nhau, nhưng khai báo phương thức / hàm trông rất giống nhau trong hầu hết các ngôn ngữ, vì vậy tôi nghĩ có thể ai đó đã triển khai tính năng này.

Ngôn ngữ tôi hiện đang làm việc là Objective-C và SCM tôi hiện đang sử dụng là git, nhưng tôi muốn biết liệu tính năng này có tồn tại cho bất kỳ SCM / ngôn ngữ nào hay không.


1
Tôi đã thấy chức năng như vậy trong đề xuất Git GSoG.
Vi.

Đây có phải là đề xuất bạn đang nói về? list-archives.org/git/…
Erik B


@lpapp câu hỏi ở đây đã được hỏi 10 tháng trước đó, câu hỏi khác nên được đánh dấu là dupe của câu hỏi này (nếu chúng hoàn toàn là lừa đảo).
Dirty-flow

2
@lpapp Đó là hai câu hỏi hoàn toàn khác nhau. Bạn có thể viết một tập lệnh phân giải tên hàm thành một phạm vi dòng và sau đó sử dụng kỹ thuật từ câu hỏi đó để lấy lịch sử cho các dòng đó, nhưng bản thân nó không trả lời câu hỏi này.
Erik B

Câu trả lời:


97

Các phiên bản gần đây của git logđã học một dạng đặc biệt của -Ltham số:

-L: <funcname>: <file>

Theo dõi sự phát triển của phạm vi dòng được cung cấp bởi "<start>,<end>"(hoặc tên hàm regex <funcname>) trong <file>. Bạn không thể đưa ra bất kỳ giới hạn pathspec nào. Điều này hiện được giới hạn cho một cuộc dạo chơi bắt đầu từ một bản sửa đổi, tức là bạn chỉ có thể đưa ra không hoặc một đối số sửa đổi khẳng định. Bạn có thể chỉ định tùy chọn này nhiều lần.
...
Nếu “:<funcname>”được đưa ra thay cho <start><end>, nó là một biểu thức chính quy biểu thị phạm vi từ dòng funcname đầu tiên phù hợp <funcname>, cho đến dòng funcname tiếp theo. “:<funcname>”tìm kiếm từ cuối -Lphạm vi trước đó , nếu có, nếu không từ đầu tệp. “^:<funcname>”tìm kiếm từ đầu tệp.

Nói cách khác: nếu bạn yêu cầu Git git log -L :myfunction:path/to/myfile.c, giờ đây nó sẽ in lịch sử thay đổi của chức năng đó.


15
điều này có thể hoạt động với mục tiêu-c ngoài hộp nhưng nếu bạn đang làm điều này cho các ngôn ngữ khác (ví dụ: Python, Ruby, v.v.), bạn có thể cần thêm cấu hình thích hợp bên trong tệp .gitattributes để git nhận dạng hàm / phương thức khai báo bằng ngôn ngữ đó. Để sử dụng python * .py diff = python, để sử dụng ruby * rb diff = ruby
samaspin

1
Làm thế nào để git theo dõi chức năng?
nn0p

@ nn0p Tôi giả sử bằng cách có kiến ​​thức cú pháp của một số ngôn ngữ và do đó biết cách tách một hàm và theo dõi các thay đổi của nó.
JasonGenX

4
Mở rộng @ bình luận samaspin của, cho các ngôn ngữ khác mà bạn có thể tham khảo các tài liệu ở đây: git-scm.com/docs/gitattributes#_generating_diff_text
edhgoose

Điều này không hoạt động đối với các ngôn ngữ như Scala và Java và thậm chí cả C sử dụng dấu ngoặc nhọn lồng nhau (biểu thức chính quy không thể xử lý điều đó nói chung) và ngay cả biểu mẫu bắt đầu, kết thúc không hoạt động khi hàm được di chuyển trong tệp và được sửa đổi trong cùng một cam kết.
Robin Green

16

Việc sử dụng git gui blamerất khó để sử dụng trong các tập lệnh, và trong khi git log -Ggit log --pickaxemỗi tập lệnh có thể hiển thị cho bạn khi nào định nghĩa phương thức xuất hiện hoặc biến mất, tôi không tìm thấy bất kỳ cách nào để liệt kê tất cả các thay đổi được thực hiện đối với phần thân phương thức của bạn.

Tuy nhiên, bạn có thể sử dụng gitattributestextconvtài sản để ghép thành một giải pháp thực hiện điều đó. Mặc dù ban đầu các tính năng này nhằm giúp bạn làm việc với các tệp nhị phân, nhưng chúng hoạt động tốt ở đây.

Điều quan trọng là phải xóa Git khỏi tệp tất cả các dòng ngoại trừ những dòng bạn quan tâm trước khi thực hiện bất kỳ thao tác khác biệt nào. Sau đó git log, git diffv.v. sẽ chỉ thấy khu vực bạn quan tâm.

Đây là phác thảo những gì tôi làm bằng ngôn ngữ khác; bạn có thể điều chỉnh nó cho nhu cầu của riêng bạn.

  • Viết một tập lệnh shell ngắn (hoặc chương trình khác) lấy một đối số - tên của tệp nguồn - và chỉ xuất phần thú vị của tệp đó (hoặc không có gì nếu không có phần nào thú vị). Ví dụ, bạn có thể sử dụng sednhư sau:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
  • Xác định textconvbộ lọc Git cho tập lệnh mới của bạn. (Xem gitattributestrang man để biết thêm chi tiết.) Tên của bộ lọc và vị trí của lệnh có thể là bất cứ thứ gì bạn thích.

    $ git config diff.my_filter.textconv /path/to/my_script
  • Yêu cầu Git sử dụng bộ lọc đó trước khi tính toán khác biệt cho tệp được đề cập.

    $ echo "my_file diff=my_filter" >> .gitattributes
  • Bây giờ, nếu bạn sử dụng -G.(lưu ý .) để liệt kê tất cả các cam kết tạo ra các thay đổi có thể nhìn thấy khi bộ lọc của bạn được áp dụng, bạn sẽ có chính xác các cam kết mà bạn quan tâm. Bất kỳ tùy chọn nào khác sử dụng các thói quen khác biệt của Git, chẳng hạn như --patch, sẽ cũng nhận được chế độ xem bị hạn chế này.

    $ git log -G. --patch my_file
  • Voilà!

Một cải tiến hữu ích mà bạn có thể muốn thực hiện là đặt tập lệnh bộ lọc của bạn lấy tên phương thức làm đối số đầu tiên (và tệp là đối số thứ hai của nó). Điều này cho phép bạn chỉ định một phương pháp quan tâm mới chỉ bằng cách gọi git config, thay vì phải chỉnh sửa tập lệnh của bạn. Ví dụ, bạn có thể nói:

$ git config diff.my_filter.textconv "/path/to/my_command other_func"

Tất nhiên, tập lệnh bộ lọc có thể làm bất cứ điều gì bạn thích, lấy thêm đối số hoặc bất cứ điều gì: có rất nhiều sự linh hoạt ngoài những gì tôi đã trình bày ở đây.


1
Đối số lấy nhiều hơn một không hoạt động đối với tôi, nhưng đặt tên hàm trong các công việc khó khăn và điều này thực sự tuyệt vời!
qwertzguy

Brilliant, mặc dù tôi tự hỏi làm thế nào (trong) thuận tiện để chuyển đổi giữa nhiều phương pháp khác nhau. Ngoài ra, bạn có biết về một chương trình có thể kéo ra toàn bộ một hàm giống C không?
nafg,

12

git log có một tùy chọn '-G' có thể được sử dụng để tìm tất cả các điểm khác biệt.

-G Tìm sự khác biệt có dòng được thêm vào hoặc loại bỏ phù hợp với dòng đã cho <regex>.

Chỉ cần cung cấp cho nó một regex thích hợp của tên hàm mà bạn quan tâm. Ví dụ,

$ git log --oneline -G'^int commit_tree'
40d52ff make commit_tree a library function
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
7b9c0a6 git-commit-tree: make it usable from other builtins

5
Tôi đã không chạy lệnh, nhưng đối với tôi thì có vẻ như lệnh này sẽ chỉ hiển thị các cam kết chạm vào dòng khớp với regex, không phải là toàn bộ phương thức / hàm.
Erik B

nếu bạn muốn có thêm ngữ cảnh, bạn có thể thay thế --onelinebằng-p
lfender6445

3
Nhưng điều gì sẽ xảy ra nếu một thay đổi được thực hiện 20 dòng trong phương thức?
nafg

+1. Đối với tôi, câu trả lời hàng đầu đã hoạt động nhưng chỉ hiển thị cam kết gần đây nhất. Có thể là do sự cố hoặc chức năng di chuyển khoảng một vài lần, không chắc chắn. Bằng cách tìm kiếm dòng mã thực tế thay vì chức năng của nó (mặc dù là một dòng chữ), câu trả lời này cho phép tôi dễ dàng phát hiện ra cam kết mà tôi đang tìm kiếm. Rất may, tôi thường viết các thông điệp cam kết hữu ích!
Dave S

11

Điều gần nhất bạn có thể làm là xác định vị trí của hàm trong tệp (ví dụ: giả sử hàm của bạn i_am_buggynằm ở dòng 241-263 trong tổng số foo/bar.c), sau đó chạy một cái gì đó có hiệu lực:

git log -p -L 200,300:foo/bar.c

Điều này sẽ mở ít hơn (hoặc một máy nhắn tin tương đương). Bây giờ bạn có thể nhập /i_am_buggy(hoặc máy nhắn tin tương đương của bạn) và bắt đầu thực hiện các thay đổi.

Điều này thậm chí có thể hoạt động, tùy thuộc vào kiểu mã của bạn:

git log -p -L /int i_am_buggy\(/,+30:foo/bar.c

Điều này giới hạn tìm kiếm từ lần truy cập đầu tiên của regex đó (lý tưởng là khai báo hàm của bạn) trong ba mươi dòng sau đó. Đối số kết thúc cũng có thể là một regexp, mặc dù phát hiện ra rằng với regexp's là một mệnh đề khó hơn.


Ngọt! FYI, đây là tính năng mới trong Git v1.8.4. (Đoán là tôi nên nâng cấp.) Mặc dù một giải pháp chính xác hơn sẽ rất hay ... giống như nếu ai đó viết ra câu trả lời của Paul Whittaker.
Giá Greg

@GregPrice Rõ ràng các cạnh của tìm kiếm thậm chí có thể là các biểu thức chính quy , vì vậy ít nhất bạn có thể có một điểm bắt đầu chính xác hơn hoặc ít hơn.
badp

Tuyệt vời. Trên thực tế: thay vì viết regexp của riêng bạn, bạn chỉ có thể nói -L ":int myfunc:foo/bar.c"và giới hạn hàm với tên đó. Điều này thật tuyệt vời - cảm ơn vì con trỏ! Bây giờ nếu chỉ phát hiện chức năng là một chút đáng tin cậy hơn ...
Greg Giá

3

Cách chính xác là sử dụng git log -L :function:path/to/filenhư giải thích trong câu trả lời eckes .

Nhưng ngoài ra, nếu hàm của bạn rất dài, bạn có thể chỉ muốn xem những thay đổi mà các cam kết khác nhau đã giới thiệu, chứ không phải toàn bộ các dòng chức năng, bao gồm chưa sửa đổi, đối với mỗi cam kết có thể chỉ chạm vào một trong các dòng này. Giống như một điều bình thường diff.

Bình thường git logcó thể xem sự khác biệt với -p, nhưng điều này không hoạt động với -L. Vì vậy, bạn phải grep git log -Lchỉ hiển thị các dòng liên quan và tiêu đề commit / files để ngữ cảnh hóa chúng. Bí quyết ở đây là chỉ kết hợp các đường màu đầu cuối, thêm --colorcông tắc, với một regex. Cuối cùng:

git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3

Lưu ý rằng ^[phải thực tế, theo nghĩa đen ^[. Bạn có thể nhập chúng bằng cách nhấn ^ V ^ [trong bash, nghĩa là Ctrl+ V, Ctrl+ [. Tham khảo tại đây .

Cũng -3công tắc cuối cùng , cho phép in 3 dòng ngữ cảnh đầu ra, trước và sau mỗi dòng phù hợp. Bạn có thể muốn điều chỉnh nó theo nhu cầu của mình.


2

git blu cho bạn biết ai đã thay đổi từng dòng cuối cùng của tệp; bạn có thể chỉ định các dòng để kiểm tra để tránh nhận được lịch sử của các dòng bên ngoài chức năng của bạn.


4
với git gui blamebạn có thể điều hướng các phiên bản cũ hơn.
Vi.

0
  1. Hiển thị lịch sử chức năng với git log -L :<funcname>:<file>như được hiển thị trong câu trả lời của eckesgit doc

    Nếu nó không hiển thị gì, hãy tham khảo Xác định một tiêu đề tập trung tùy chỉnh để thêm một cái gì đó giống như *.java diff=javavào .gitattributes tệp để hỗ trợ ngôn ngữ của bạn.

  2. Hiển thị lịch sử chức năng giữa các lần cam kết với git log commit1..commit2 -L :functionName:filePath

  3. Hiển thị lịch sử hàm quá tải (có thể có nhiều hàm có cùng tên, nhưng với các tham số khác nhau) với git log -L :sum\(double:filepath

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.