2008-11-10 26 views
51

Chỉ cần nói rằng tôi có một tệp: "HelloWorld.pm" trong nhiều thư mục con trong kho lưu trữ Git.Tôi có thể sử dụng Git để tìm kiếm tên tệp phù hợp trong kho lưu trữ không?

Tôi muốn đưa ra một lệnh để tìm ra đường dẫn đầy đủ của tất cả các tập tin phù hợp với "HelloWorld.pm":

Ví dụ:

/path/to/repository/HelloWorld.pm 
/path/to/repository/but/much/deeper/down/HelloWorld.pm 
/path/to/repository/please/dont/make/me/search/through/the/lot/HelloWorld.pm 

Làm thế nào tôi có thể sử dụng Git để có hiệu quả tất cả đường dẫn đầy đủ khớp với tên tệp đã cho?

Tôi nhận ra mình có thể làm điều này với lệnh tìm Linux/Unix nhưng tôi đã hy vọng tránh quét tất cả các thư mục con tìm kiếm các phiên bản của tên tệp.

Trả lời

19

Hãy thử:

git ls-tree -r HEAD | grep HelloWorld.pm 
+1

Hoặc trên Windows: 'git ls-cây -r TRỤ | findstr HelloWorld.pm' –

79

git ls-files sẽ cung cấp cho bạn một danh sách của tất cả các file trong kho. Bạn có thể chuyển mẫu vào để nhận các tệp phù hợp với mẫu đó.

git ls-files '*/HelloWorld.pm' 

Nếu bạn muốn tìm một tập hợp các tập tin và grep thông qua nội dung của họ, bạn có thể làm điều đó với git grep:

git grep some-string -- '*/HelloWorld.pm' 
+0

ls-files cũng có thể lấy mẫu. –

+0

@jleedev Ah, phải. Cập nhật câu trả lời của tôi để đơn giản hóa nó và sửa một vấn đề với mẫu trong 'git grep'. –

+0

(Khó chịu, nó được gọi là [pathspec] (http://www.kernel.org/pub/software/scm/git/docs/gitglossary.html#def_pathspec) trong gitglossary (7), nhưng thuật ngữ đó không được sử dụng nhất quán ở những nơi khác.) –

38

Hmm, câu hỏi ban đầu là về kho. Một kho lưu trữ chứa nhiều hơn 1 cam kết (trong trường hợp chung ít nhất), nhưng các câu trả lời được đưa ra trước khi tìm kiếm chỉ thông qua một cam kết.

Bởi vì tôi không thể tìm thấy câu trả lời thực sự tìm kiếm toàn bộ lịch sử cam kết, tôi đã viết một kịch bản nhanh gọn git-find-by-name mà cần (gần) tất cả các cam kết xem xét.

#! /bin/sh 
tmpdir=$(mktemp -td git-find.XXXX) 
trap "rm -r $tmpdir" EXIT INT TERM 

allrevs=$(git rev-list --all) 
# well, nearly all revs, we could still check the log if we have 
# dangling commits and we could include the index to be perfect... 

for rev in $allrevs 
do 
    git ls-tree --full-tree -r $rev >$tmpdir/$rev 
done 

cd $tmpdir 
grep $1 * 

Có thể có cách thanh lịch hơn.

Xin lưu ý rằng cách tầm thường tham số được chuyển vào grep, vì vậy nó sẽ khớp với các phần của tên tệp. Nếu đó không phải là mong muốn neo biểu thức tìm kiếm của bạn và/hoặc thêm các tùy chọn grep phù hợp.

Đối với lịch sử sâu, đầu ra có thể quá ồn ào, tôi nghĩ về một tập lệnh chuyển đổi một danh sách các sửa đổi thành một phạm vi, như đối diện với những gì git rev-list có thể làm. Nhưng cho đến nay nó vẫn là một ý nghĩ.

+1

Cảm ơn rất nhiều vì câu trả lời tuyệt vời này. Nó đã cứu ngày của tôi. Tôi ước tôi có thể upvote bạn nhiều hơn nữa. –

+0

Kịch bản tuyệt vời. Tuy nhiên tôi không thể sử dụng nó bởi vì repo git của tôi quá lớn đến nỗi kịch bản tràn ngập ổ đĩa cứng của tôi: ( –

+0

@ ArneBöckmann Chỉ cần di chuyển lệnh grep vào vòng cuối cùng và xóa mọi thứ sau mỗi grep. –

3

[Đó là một chút lạm dụng comment, tôi thừa nhận, nhưng tôi không thể bình luận được nêu ra và nghĩ tôi sẽ cải thiện câu trả lời @ Uwe-geuder của.]

#!/bin/bash 
# 
# 

# I'm using a fixed string here, not a regular expression, but you can easily 
# use a regular expression by altering the call to grep below. 
name="$1" 

# Verify usage. 
if [[ -z "$name" ]] 
then 
    echo "Usage: $(basename "$0") <file name>" 1>&2 
    exit 100 
fi 

# Search all revisions; get unique results. 
while IFS= read rev 
do 
    # Find $name in $rev's tree and only use its path. 
    grep -F -- "$name" \ 
     <(git ls-tree --full-tree -r "$rev" | awk '{ print $4 }') 
done < \ 
    <(git rev-list --all) \ 
    | sort -u 

Một lần nữa, 1 để @ Uwe-geuder cho một câu trả lời tuyệt vời.

Nếu bạn quan tâm trong BASH bản thân:

Trừ khi bạn đang đảm bảo của từ-chia trong một vòng lặp for (như khi sử dụng một mảng như thế này: for item in "${array[@]}"), tôi khuyên bạn nên sử dụng while IFS= read var ; do ... ; done < <(command) khi đầu ra lệnh bạn đang lặp lại được phân tách bằng các dòng mới (hoặc read -d'' khi đầu ra được phân tách bằng chuỗi rỗng $'\0'). Trong khi git rev-list --all được đảm bảo sử dụng chuỗi thập lục phân 40 byte (không có khoảng trống), tôi không bao giờ muốn nắm lấy cơ hội.Bây giờ tôi có thể dễ dàng thay đổi lệnh từ git rev-list --all thành bất kỳ lệnh nào tạo ra dòng

Tôi cũng khuyên bạn nên sử dụng cơ chế BASH được tích hợp để chèn đầu vào và đầu ra bộ lọc thay vì tệp tạm thời.

+0

Không chắc chắn lý do tại sao quá nhiều thay thế quá trình đang được sử dụng, khi bạn chỉ có thể ống: 'git rev-list --all | trong khi đọc rev; do; git ls-tree - full-tree -r $ rev | cắt -c54- | fgrep - "$ name"; done | sort -u' –

7
git ls-files | grep -i HelloWorld.pm 

Grep -i làm cho trường hợp grep không nhạy cảm.

0

Kịch bản của Uwe Geuder (@ uwe-geuder) là tuyệt vời nhưng thực sự không cần phải đổ từng kết quả đầu ra ls-tree trong thư mục riêng của nó, chưa được lọc.

Phần lớn nhanh hơn và sử dụng ít lưu trữ: Chạy grep trên đầu ra và sau đó lưu nó, như thể hiện trong này gist

Các vấn đề liên quan