Tìm một nhánh Git chứa các thay đổi đối với một tệp nhất định


88

Tôi có 57 chi nhánh địa phương. Tôi biết mình đã thực hiện thay đổi đối với một tệp nhất định ở một trong số chúng, nhưng tôi không chắc cái nào. Có loại lệnh nào tôi có thể chạy để tìm các nhánh nào chứa các thay đổi đối với một tệp nhất định không?

Câu trả lời:


87

Tìm tất cả các nhánh có thay đổi đối với FILENAME (ngay cả khi trước điểm nhánh (không được ghi))

FILENAME="<filename>"
git log --all --format=%H $FILENAME | while read f; do git branch --contains $f; done | sort -u

Kiểm tra thủ công:

gitk --all --date-order -- $FILENAME

Tìm tất cả các thay đổi đối với FILENAME chưa được hợp nhất thành trang cái:

git for-each-ref --format="%(refname:short)" refs/heads | grep -v master | while read br; do git cherry master $br | while read x h; do if [ "`git log -n 1 --format=%H $h -- $FILENAME`" = "$h" ]; then echo $br; fi; done; done | sort -u

Tôi có cần thay thế bất kỳ điều gì trong lệnh này ngoài FILENAME không? Nó trả về tất cả 57 tên chi nhánh.
Dustin

@Dustin: Nếu tất cả 57 nhánh chứa tên tệp đó, thì tất cả 57 nhánh chứa một thay đổi bao gồm tên tệp đó. Các nhánh bắt đầu từ bản sửa đổi cũ nhất có thể truy cập được trên nhánh đó, ngay cả khi bản sửa đổi đó tồn tại trước khi bạn tạo nhánh (theo một số nghĩa là nhánh được tạo từ trước). Tôi nghĩ bạn cần xác định rõ vấn đề của mình hơn. Bạn có biết thay đổi là gì không? Bạn có thể sử dụng git log -Schange …hoặc git log --grep LOGMESSAGE …(với… đại diện cho phần còn lại của lệnh mà tôi đã đề cập).
Seth Robertson

2
@Dustin: Một tùy chọn khác là sử dụng tùy chọn này gitk --all -- filenamesẽ hiển thị bằng đồ thị cho bạn tất cả các thay đổi đối với tệp đó. Nếu bạn có thể xác định cam kết được đề cập, thì bạn có thể sử dụng git branch --containsđể xem cam kết đã di chuyển đến những nhánh nào. Nếu bạn muốn xem chi nhánh mà cam kết được đề cập ban đầu được tạo trên google git-what-branch, nhưng lưu ý rằng các hợp nhất tua đi nhanh có thể che khuất thông tin đó.
Seth Robertson

@Seth: Tôi không thể nhớ thông báo cam kết hoặc thay đổi mã chính xác. Tôi chỉ có một ý tưởng chung. Vấn đề là nó chưa được hợp nhất với master, vì vậy để sử dụng gitk, tôi vẫn sẽ phải kiểm tra từng nhánh để tìm những gì tôi đang tìm kiếm. Thật tuyệt nếu tôi có thể nói, "git, chỉ cho tôi các nhánh đã thực hiện thay đổi đối với FILENAME kể từ điểm nhánh của nó"
Dustin

1
Bạn có thể viết những dòng đầu tiên hiệu quả hơngit log --all --format='--contains %H' "$file" | xargs git branch
Kojiro

52

Tất cả bạn cần là

git log --all -- path/to/file/filename

Nếu bạn muốn biết chi nhánh ngay lập tức, bạn cũng có thể sử dụng:

git log --all --format=%5 -- path/to/file/filename | xargs -I{} -n 1 echo {} found in && git branch --contains {}

Hơn nữa, nếu bạn có bất kỳ tên nào, bạn có thể muốn đưa --followvào lệnh nhật ký Git.


15
Điều đó sẽ hiển thị các cam kết, nhưng anh ấy hỏi chi nhánh nào. Thêm một --sourcevào đó và bạn vàng.
Karl Bielefeldt

Cảm ơn, nhưng tôi không nghĩ rằng điều đó hiển thị tất cả các nhánh cho mỗi cam kết. Nhưng nó sẽ hữu ích.
Adam Dymitruk

1
Đúng, nhưng trong trường hợp cụ thể này, anh ấy chỉ tìm kiếm một.
Karl Bielefeldt

btw cho asp.net con đường phải ở định dạng như AppName / controllers / XController.cs
Ali Karaca

8

Có vẻ như đây vẫn là một vấn đề nan giải chưa có lời giải thích hợp. Tôi không có đủ tín dụng để bình luận, vì vậy đây là đóng góp nhỏ của tôi.

Giải pháp đầu tiên của Seth Robertson khá phù hợp với tôi, nhưng chỉ cung cấp cho tôi các nhánh cục bộ, trong đó có nhiều kết quả dương tính giả, có thể là do hợp nhất từ ​​nhánh ổn định.

Giải pháp thứ 2 của Adam Dymitruk không phù hợp với tôi. Đối với người mới bắt đầu, --format =% 5 là gì? Nó không được git nhận dạng, tôi không thể tìm thấy gì về nó và tôi không thể làm cho nó hoạt động với các tùy chọn định dạng khác.

Nhưng giải pháp đầu tiên của anh ấy kết hợp với tùy chọn --source và với một grep đơn giản đã tỏ ra hữu ích:

git log --all --source -- <filename> | grep -o "refs/.*" | sort -u

Điều này cung cấp cho tôi một số thẻ và nhánh từ xa và một nhánh cục bộ, nơi tôi thực hiện những thay đổi mới nhất đối với tệp. Không chắc chắn rằng điều này là hoàn chỉnh như thế nào.

CẬP NHẬT theo yêu cầu của @nealmcb, sắp xếp các nhánh theo thay đổi gần đây nhất:

Đầu tiên, bạn có thể thay đổi grep thành "refs / heads /.*", điều này sẽ chỉ cung cấp cho bạn các chi nhánh cục bộ. Nếu chỉ có một vài nhánh, bạn có thể kiểm tra cam kết mới nhất của từng nhánh như sau:

git log -1 <branch> -- <filename>

Nếu có nhiều nhánh hơn và bạn thực sự muốn tự động hóa việc này, bạn có thể kết hợp hai lệnh bằng cách sử dụng xargs, định dạng git log và một cách sắp xếp khác vào một lớp lót này:

git log --all --source -- <filename> | grep -o "refs/heads/.*" | sort -u | xargs -I '{}' git log -1 --format=%aI%x20%S '{}' -- <filename> | sort -r

Điều này sẽ dẫn đến đầu ra như thế này:

2020-05-07T15:10:59+02:00 refs/heads/branch1
2020-05-05T16:11:52+02:00 refs/heads/branch3
2020-03-27T11:45:48+00:00 refs/heads/branch2

Tuyệt vời cảm ơn bạn. Bây giờ làm thế nào về việc sắp xếp theo thay đổi gần đây nhất?
nealmcb

1

Tôi biết câu hỏi này rất cổ, nhưng tôi vẫn quay lại với nó trước khi phát triển giải pháp của riêng mình. Tôi cảm thấy nó thanh lịch hơn và nhờ sử dụng cơ sở hợp nhất lọc ra các nhánh không mong muốn.

#!/bin/bash

file=$1
base=${2:-master}

b=$(tput bold) # Pretty print
n=$(tput sgr0)

echo "Searching for branches with changes to $file related to the $base branch"

# We look through all the local branches using plumbing
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do
  # We're establishing a shared ancestor between base and branch, to only find forward changes.  
  merge_base=$(git merge-base $base $branch)
  # Check if there are any changes in a given path.
  changes=$(git diff $merge_base..$branch --stat -- $file)

  if [[ ! -z $changes ]]; then
    echo "Branch: ${b}$branch${n} | Merge Base: $merge_base"
    # Show change statistics pretty formatted
    git diff $merge_base..$branch --stat -- $file
  fi
done

Nếu bạn đặt nó trong PATH là git-find-changes(với quyền thực thi) - thì bạn có thể gọi nó bằnggit find-changes /path

Ví dụ đầu ra cho git find-changes app/models/

Branch: update_callbacks | Merge base: db07d23b5d9600d88ba0864aca8fe79aad14e55b
 app/models/api/callback_config.rb | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
Branch: repackaging | Merge base: 76578b9b7ee373fbe541a9e39cf93cf5ff150c73
 app/models/order.rb                 | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

Kịch bản rất thú vị. Đã ủng hộ. Đó là thay đổi hiển thị hay thay đổi tìm kiếm?
VonC

@VonC cảm ơn, cách sử dụng đã sửa chữa, nên được tìm thấy ở mọi nơi
Marcin Raczkowski

0

Sau đây là một phương pháp bạo lực không phù hợp, nhưng tôi hy vọng nó sẽ hoạt động. Trước tiên, hãy đảm bảo rằng bạn đã lưu trữ mọi thay đổi chưa được cam kết vì nó sẽ chuyển sang nhánh nào bạn hiện đang sử dụng.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done

1
Bởi vì nếu bạn biết sự thay đổi, bạn có thể sử dụnggit log -S
Seth Robertson

Bạn đang giả sử lớp vỏ nào? Bash?
Peter Mortensen

Không có bashisms, điều đó sẽ hoạt động trên POSIX. Tôi không chắc rằng for-each-refvẫn tôn trọng shortlá cờ đó . Điều đó nói rằng, bỏ qua câu trả lời của tôi; các câu trả lời khác tốt hơn.
whiteinge
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.