Temukan cabang Git yang berisi perubahan pada file tertentu

91

Saya memiliki 57 cabang lokal. Saya tahu saya membuat perubahan pada file tertentu di salah satunya, tapi saya tidak yakin yang mana. Apakah ada semacam perintah yang dapat saya jalankan untuk menemukan cabang mana yang berisi perubahan pada file tertentu?

Dustin
sumber

Jawaban:

89

Temukan semua cabang yang berisi perubahan ke FILENAME (bahkan jika sebelum titik cabang (tidak tercatat))

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

Periksa secara manual:

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

Temukan semua perubahan pada FILENAME yang tidak digabungkan ke master:

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
Seth Robertson
sumber
Apakah saya perlu mengganti apa pun dalam perintah ini selain FILENAME? Ini mengembalikan semua 57 nama cabang.
Dustin
@Dustin: Jika semua 57 cabang berisi nama file itu, maka semua 57 cabang berisi perubahan yang menyertakan nama file itu. Cabang mulai dari revisi terlama yang dapat dijangkau pada cabang itu, meskipun revisi itu ada sebelum Anda membuat cabang (cabang dalam beberapa hal dibuat secara retroaktif). Saya pikir Anda perlu mendefinisikan masalah Anda dengan lebih baik. Tahukah Anda apa perubahannya? Bisakah Anda menggunakan git log -Schange …atau git log --grep LOGMESSAGE …(dengan ... mewakili sisa perintah yang saya sebutkan).
Seth Robertson
2
@Dustin: Opsi lain adalah menggunakan gitk --all -- filenameyang secara grafis akan menampilkan semua perubahan pada file itu. Jika Anda dapat mengidentifikasi komit yang dipermasalahkan, Anda dapat menggunakan git branch --containsuntuk melihat ke cabang mana komit telah dimigrasi. Jika Anda ingin melihat di cabang mana komit yang dipermasalahkan awalnya dibuat, maka google git-what-branch, tetapi ketahuilah bahwa penggabungan maju cepat dapat mengaburkan informasi itu.
Seth Robertson
@ Set: Saya tidak dapat mengingat pesan komit atau perubahan kode yang tepat. Saya hanya punya gambaran umum. Masalahnya adalah ini belum digabungkan dengan master, jadi untuk menggunakan gitk saya masih harus memeriksa setiap cabang untuk menemukan apa yang saya cari. Alangkah baiknya jika saya bisa mengatakan, "git, tunjukkan cabang yang membuat perubahan pada FILENAME sejak titik cabangnya"
Dustin
1
Anda dapat menulis baris pertama dengan lebih efisiengit log --all --format='--contains %H' "$file" | xargs git branch
kojiro
53

Yang kamu butuhkan adalah

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

Jika Anda ingin segera mengetahui cabangnya, Anda juga dapat menggunakan:

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

Selanjutnya, jika Anda memiliki nama baru, Anda mungkin ingin memasukkan --followperintah log Git.

Adam Dymitruk
sumber
16
Itu akan menunjukkan komitmennya, tapi dia bertanya cabang mana. Tambahkan --sourcedi sana dan Anda emas.
Karl Bielefeldt
Terima kasih, tapi saya tidak berpikir itu menunjukkan semua cabang untuk setiap komit. Tapi itu akan membantu.
Adam Dymitruk
1
Benar, tetapi dalam kasus khusus ini dia hanya mencari satu.
Karl Bielefeldt
btw untuk jalur asp.net harus dalam format seperti AppName / Controllers / XController.cs
Ali Karaca
8

Sepertinya ini masih menjadi masalah tanpa solusi yang tepat. Saya tidak memiliki cukup kredit untuk dikomentari, jadi inilah kontribusi kecil saya.

Solusi pertama Seth Robertson agak berhasil untuk saya, tetapi hanya memberi saya cabang lokal, di antaranya banyak positif palsu, mungkin karena penggabungan dari cabang stabil.

Solusi kedua Adam Dymitruk sama sekali tidak berhasil untuk saya. Sebagai permulaan, apa --format =% 5? Itu tidak dikenali oleh git, saya tidak dapat menemukan apa pun tentangnya dan saya tidak dapat membuatnya berfungsi dengan opsi format lain.

Tetapi solusi pertamanya yang dikombinasikan dengan opsi --source dan dengan grep sederhana terbukti membantu:

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

Ini memberi saya sejumlah tag dan cabang jarak jauh dan satu cabang lokal, tempat saya membuat perubahan terbaru pada file. Tidak yakin seberapa lengkapnya ini.

UPDATE sesuai permintaan @nealmcb, menyortir cabang berdasarkan perubahan terbaru:

Pertama, Anda dapat mengubah grep menjadi "refs / heads /.*", yang akan memberi Anda cabang lokal saja. Jika hanya ada beberapa cabang, Anda dapat memeriksa komit terbaru dari masing-masing cabang seperti ini:

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

Jika ada lebih banyak cabang dan Anda benar-benar ingin mengotomatiskannya, Anda dapat menggabungkan dua perintah menggunakan xargs, git log formatting dan jenis lain ke dalam satu baris ini:

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

Ini akan menghasilkan keluaran seperti ini:

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
Orang bodoh
sumber
Terima kasih banyak. Sekarang bagaimana dengan mengurutkan menurut perubahan terbaru?
nealmcb
1

Saya tahu pertanyaan ini kuno, tetapi saya terus mengulanginya sebelum mengembangkan solusi saya sendiri. Saya merasa ini lebih elegan dan berkat penggunaan filter dasar gabungan dari cabang yang tidak diinginkan.

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

Jika Anda memasukkannya ke dalam PATH sebagai git-find-changes(dengan izin yang dapat dieksekusi) - Anda kemudian dapat memanggilnya dengangit find-changes /path

Contoh keluaran untuk 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(+)
Marcin Raczkowski
sumber
Naskah yang sangat menarik. Suara positif. Apakah itu tampilkan-perubahan atau temukan-perubahan?
VonC
@VonC terima kasih, penggunaan yang dikoreksi, harus ditemukan di mana
Marcin Raczkowski
0

Berikut ini adalah metode kekerasan yang tidak elegan, tapi saya berharap itu akan berhasil. Pastikan Anda telah menyimpan perubahan yang belum terikat terlebih dahulu karena ini akan mengalihkan cabang mana yang saat ini Anda gunakan.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done
whiteinge
sumber
1
Karena jika Anda tahu perubahannya, Anda dapat menggunakangit log -S
Seth Robertson
Apa shell yang Anda asumsikan? Pesta?
Peter Mortensen
Tidak ada bashisme, yang seharusnya berfungsi di POSIX. Saya tidak yakin bahwa for-each-refmasih menghormati shortbendera itu. Karena itu, abaikan jawaban saya; jawaban lain lebih baik.
whiteinge