ค้นหาสาขา Git ที่มีการเปลี่ยนแปลงไฟล์ที่กำหนด


92

ฉันมีสาขาในประเทศ 57 สาขา ฉันรู้ว่าฉันได้ทำการเปลี่ยนแปลงไฟล์บางไฟล์ในไฟล์ใดไฟล์หนึ่ง แต่ไม่แน่ใจว่าไฟล์ใด มีคำสั่งบางประเภทที่ฉันสามารถเรียกใช้เพื่อค้นหาว่าสาขาใดมีการเปลี่ยนแปลงในไฟล์บางไฟล์หรือไม่

คำตอบ:


90

ค้นหาสาขาทั้งหมดที่มีการเปลี่ยนแปลง FILENAME (แม้ว่าจะอยู่ก่อนจุดสาขา (ไม่ได้บันทึก))

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

ตรวจสอบด้วยตนเอง:

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

ค้นหาการเปลี่ยนแปลงทั้งหมดที่ FILENAME ไม่ได้รวมเข้ากับหลัก:

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

ฉันต้องแทนที่อะไรในคำสั่งนี้นอกเหนือจาก FILENAME หรือไม่ ส่งคืนชื่อสาขาทั้งหมด 57 ชื่อ
Dustin

@Dustin: ถ้าทั้ง 57 สาขามีชื่อไฟล์นั้นทั้ง 57 สาขาจะมีการเปลี่ยนแปลงซึ่งรวมถึงชื่อไฟล์นั้นด้วย สาขาเริ่มต้นจากการแก้ไขที่เก่าแก่ที่สุดที่สามารถเข้าถึงได้ในสาขานั้นแม้ว่าการแก้ไขนั้นจะมีอยู่ก่อนที่คุณจะสร้างสาขาก็ตาม (สาขาอยู่ในประสาทสัมผัสบางอย่างที่สร้างขึ้นย้อนหลัง) ฉันคิดว่าคุณต้องกำหนดปัญหาของคุณให้ดีขึ้น คุณรู้ไหมว่าการเปลี่ยนแปลงคืออะไร? คุณสามารถใช้git log -Schange …หรือgit log --grep LOGMESSAGE …(โดยมี ... แทนคำสั่งที่เหลือที่ฉันกล่าวถึง)
Seth Robertson

2
@Dustin: อีกทางเลือกหนึ่งคือใช้gitk --all -- filenameซึ่งจะแสดงการเปลี่ยนแปลงทั้งหมดของไฟล์นั้นแบบกราฟิก หากคุณสามารถระบุการคอมมิตที่เป็นปัญหาได้คุณสามารถใช้git branch --containsเพื่อดูว่าการคอมมิตได้ย้ายไปสาขาใด หากคุณต้องการที่จะเห็นสิ่งสาขากระทำในคำถามถูกแต่เดิมสร้างขึ้นบนแล้วคอมไพล์ google สิ่งสาขา แต่ทราบว่าการผสานข้างหน้าอย่างรวดเร็วสามารถปิดบังข้อมูลว่า
Seth Robertson

@Seth: ฉันจำข้อความคอมมิตหรือเปลี่ยนรหัสไม่ได้ ฉันแค่มีความคิดทั่วไป ปัญหาคือยังไม่ได้รวมกับ master ดังนั้นในการใช้ gitk ฉันยังคงต้องชำระเงินแต่ละสาขาเพื่อค้นหาสิ่งที่ฉันกำลังมองหา คงจะดีถ้าฉันสามารถพูดว่า "git ขอดูสาขาที่เปลี่ยนแปลง FILENAME ตั้งแต่จุดสาขา"
Dustin

1
คุณสามารถเขียนบรรทัดแรกได้อย่างมีประสิทธิภาพมากขึ้นgit log --all --format='--contains %H' "$file" | xargs git branch
kojiro

53

สิ่งที่คุณต้องการคือ

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

หากคุณต้องการทราบสาขาทันทีคุณสามารถใช้:

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

นอกจากนี้หากคุณมีการเปลี่ยนชื่อคุณอาจต้องการรวม--followไว้สำหรับคำสั่งบันทึก Git


16
นั่นจะแสดงคอมมิต แต่เขาถามว่าสาขาไหน เพิ่ม--sourceที่นั่นและคุณเป็นสีทอง
Karl Bielefeldt

ขอบคุณ แต่ฉันไม่คิดว่าจะแสดงทุกสาขาสำหรับการคอมมิตแต่ละครั้ง แต่มันจะช่วยได้
Adam Dymitruk

1
จริง แต่ในกรณีนี้เขามองหาเพียงสิ่งเดียว
Karl Bielefeldt

btw สำหรับเส้นทาง asp.net ควรอยู่ในรูปแบบเช่น AppName / Controllers / XController.cs
Ali Karaca

8

ดูเหมือนว่านี่จะยังคงเป็นปัญหาโดยไม่มีแนวทางแก้ไขที่เหมาะสม ฉันมีเครดิตไม่เพียงพอที่จะแสดงความคิดเห็นนี่คือผลงานเล็กน้อยของฉัน

วิธีแก้ปัญหาแรกของ Seth Robertson ใช้ได้ผลสำหรับฉัน แต่ให้เฉพาะสาขาในท้องถิ่นเท่านั้นซึ่งมีผลบวกปลอมจำนวนมากอาจเป็นเพราะการรวมจากสาขาที่มั่นคง

โซลูชันที่ 2 ของ Adam Dymitruk ไม่ได้ผลสำหรับฉันเลย สำหรับผู้เริ่มต้น --format =% 5 คืออะไร คอมไพล์ไม่รู้จักฉันไม่พบอะไรเลยและไม่สามารถใช้งานกับตัวเลือกรูปแบบอื่นได้

แต่โซลูชันแรกของเขารวมกับอ็อพชัน --source และด้วย grep ง่ายๆพิสูจน์แล้วว่ามีประโยชน์:

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

สิ่งนี้ทำให้ฉันมีแท็กและสาขาระยะไกลจำนวนหนึ่งและสาขาในพื้นที่หนึ่งสาขาซึ่งฉันได้ทำการเปลี่ยนแปลงล่าสุดกับไฟล์ ไม่แน่ใจว่าสมบูรณ์แค่ไหน

อัปเดตตามคำขอของ @nealmcb เรียงลำดับสาขาตามการเปลี่ยนแปลงล่าสุด:

ขั้นแรกคุณสามารถเปลี่ยน grep เป็น "refs / head /.*" ซึ่งจะให้เฉพาะสาขาในประเทศเท่านั้น หากมีเพียงไม่กี่สาขาคุณสามารถตรวจสอบการกระทำล่าสุดของแต่ละสาขาได้ดังนี้:

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

หากมีสาขามากขึ้นและคุณต้องการทำให้เป็นอัตโนมัติคุณสามารถรวมสองคำสั่งโดยใช้ xargs, การจัดรูปแบบบันทึกคอมไพล์และการจัดเรียงอื่น ๆ ลงในซับเดียวนี้:

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

ซึ่งจะทำให้ได้ผลลัพธ์ดังนี้:

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

เยี่ยม - ขอบคุณ ตอนนี้การจัดเรียงตามการเปลี่ยนแปลงล่าสุดเป็นอย่างไร
nealmcb

1

ฉันรู้ว่าคำถามนี้เป็นคำถามเก่าแก่ แต่ฉันก็ยังคงกลับไปหามันก่อนที่จะพัฒนาวิธีแก้ปัญหาของตัวเอง ฉันรู้สึกว่ามันดูหรูหรามากขึ้นและต้องขอบคุณการใช้ merge base กรองกิ่งก้านที่ไม่ต้องการออกไป

#!/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

หากคุณวางไว้ใน PATH เป็นgit-find-changes(ด้วยสิทธิ์ที่เรียกใช้งานได้) - คุณสามารถเรียกมันด้วยไฟล์git find-changes /path

ตัวอย่างผลลัพธ์สำหรับ 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(+)

สคริปต์ที่น่าสนใจมาก โหวตแล้ว แสดงการเปลี่ยนแปลงหรือค้นหาการเปลี่ยนแปลง?
VonC

@VonC ขอบคุณการใช้งานที่ถูกต้องควรพบได้ทุกที่
Marcin Raczkowski

0

ต่อไปนี้เป็นวิธีการบังคับแบบเดรัจฉานที่ไม่สง่างาม แต่ฉันคาดว่ามันน่าจะใช้ได้ ตรวจสอบให้แน่ใจว่าคุณได้ซ่อนการเปลี่ยนแปลงที่ไม่ได้ผูกมัดไว้ก่อนเนื่องจากจะเปลี่ยนสาขาที่คุณอยู่ในปัจจุบัน

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

1
เพราะถ้าคุณรู้การเปลี่ยนแปลงคุณก็ใช้ได้git log -S
Seth Robertson

คุณคิดว่าเปลือกอะไร? ทุบตี?
Peter Mortensen

ไม่มีการทุบตีที่ควรใช้กับ POSIX ฉันไม่แน่ใจว่าfor-each-refยังคงเคารพshortธงนั้นอยู่ ที่กล่าวว่าไม่สนใจคำตอบของฉัน คำตอบอื่น ๆ ดีกว่า
whiteinge
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.