Znajdź gałąź Git zawierającą zmiany w podanym pliku

88

Mam 57 lokalnych oddziałów. Wiem, że dokonałem zmiany w pewnym pliku w jednym z nich, ale nie jestem pewien, który. Czy jest jakieś polecenie, które mogę uruchomić, aby znaleźć gałęzie zawierające zmiany w określonym pliku?

Dustin
źródło

Odpowiedzi:

87

Znajdź wszystkie gałęzie, które zawierają zmianę w FILENAME (nawet jeśli przed (nie zarejestrowanym) punktem rozgałęzienia)

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

Sprawdź ręcznie:

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

Znajdź wszystkie zmiany w FILENAME, które nie zostały scalone z wzorcem:

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
źródło
Czy muszę zamienić cokolwiek w tym poleceniu poza NAZWĄ PLIKU? Zwraca wszystkie 57 nazw gałęzi.
Dustin
@Dustin: Jeśli wszystkie 57 gałęzi zawiera tę nazwę pliku, to wszystkie 57 gałęzi zawiera zmianę, która obejmuje tę nazwę pliku. Gałęzie zaczynają się od najstarszej wersji dostępnej w tej gałęzi, nawet jeśli ta wersja istniała przed utworzeniem gałęzi (gałąź jest w pewnym sensie tworzona wstecz). Myślę, że musisz lepiej zdefiniować swój problem. Czy wiesz, co to za zmiana? Czy mógłbyś użyć git log -Schange …lub git log --grep LOGMESSAGE …(z… reprezentującym resztę polecenia, o którym wspomniałem).
Seth Robertson
2
@Dustin: Inną opcją jest użycie, gitk --all -- filenamektóre pokaże graficznie wszystkie zmiany w tym pliku. Jeśli możesz zidentyfikować dane zatwierdzenie, możesz użyć, git branch --containsaby zobaczyć, do jakich gałęzi zostało przeniesione zatwierdzenie. Jeśli chcesz zobaczyć, w której gałęzi zostało pierwotnie utworzone dane zatwierdzenie , skorzystaj z Google git-what-branch, ale pamiętaj, że szybkie scalanie może zasłonić te informacje.
Seth Robertson
@Seth: Nie pamiętałem komunikatu o zatwierdzeniu ani dokładnej zmiany kodu. Mam tylko ogólny pomysł. Problem w tym, że nie jest jeszcze scalony z masterem, więc aby używać gitk, musiałbym jeszcze sprawdzić każdą gałąź, aby znaleźć to, czego szukam. Byłoby miło, gdybym mógł powiedzieć „git, pokaż mi gałęzie, które wprowadziły zmiany w NAZWIE PLIKU od momentu jej rozgałęzienia”
Dustin
1
Mógłbyś sprawniej napisać pierwszą linięgit log --all --format='--contains %H' "$file" | xargs git branch
kojiro
52

Wszystko czego potrzebujesz to

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

Jeśli chcesz od razu poznać oddział, możesz również skorzystać z:

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

Ponadto, jeśli masz jakieś zmiany nazw, możesz chcieć dołączyć je --followdo polecenia dziennika Git.

Adam Dymitruk
źródło
15
To pokaże zatwierdzenia, ale zapytał, która gałąź. Dodaj --sourcetam i jesteś złoty.
Karl Bielefeldt
Dzięki, ale nie sądzę, żeby to wyświetlało wszystkie gałęzie dla każdego zatwierdzenia. Ale to by pomogło.
Adam Dymitruk
1
To prawda, ale w tym konkretnym przypadku szukał tylko jednego.
Karl Bielefeldt
btw dla ścieżki asp.net powinna mieć format podobny do AppName / Controllers / XController.cs
Ali Karaca
8

Wygląda na to, że jest to nadal problem bez odpowiedniego rozwiązania. Nie mam wystarczającej liczby kredytów do skomentowania, więc oto mój mały wkład.

Pierwsze rozwiązanie Setha Robertsona trochę zadziałało, ale dało mi tylko lokalne oddziały, wśród których było wiele fałszywych alarmów, prawdopodobnie z powodu fuzji ze stabilnej gałęzi.

Drugie rozwiązanie Adama Dymitruka w ogóle mi się nie udało. Na początek, co to jest --format =% 5? Nie jest rozpoznawany przez git, nie mogłem nic znaleźć na ten temat i nie mogłem go zmusić do pracy z innymi opcjami formatu.

Ale jego pierwsze rozwiązanie w połączeniu z opcją --source i prostym grepem okazało się pomocne:

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

To daje mi kilka zdalnych tagów i gałęzi oraz jeden oddział lokalny, w którym wprowadziłem najnowsze zmiany w pliku. Nie wiem, jak kompletne jest to.

AKTUALIZUJ zgodnie z żądaniem @nealmcb, sortowanie gałęzi według ostatniej zmiany:

Najpierw możesz zmienić grep na „refs / heads /.*”, co da ci tylko lokalne gałęzie. Jeśli jest tylko kilka gałęzi, możesz sprawdzić najnowsze zatwierdzenie każdej z nich w następujący sposób:

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

Jeśli jest więcej gałęzi i naprawdę chcesz to zautomatyzować, możesz połączyć te dwa polecenia za pomocą xargs, formatowania dziennika git i innego sortowania w jedną linijkę:

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

Spowoduje to następujący wynik:

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
Prostak
źródło
Wielkie dzięki. A co z sortowaniem według ostatniej zmiany?
nealmcb
1

Wiem, że to pytanie jest starożytne, ale wracałem do niego, zanim opracowałem własne rozwiązanie. Uważam, że jest bardziej elegancki i dzięki zastosowaniu bazy scalającej odfiltrowuje niechciane gałęzie.

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

Jeśli umieścisz go w PATH as git-find-changes(z uprawnieniami do plików wykonywalnych) - możesz to wywołać za pomocągit find-changes /path

Przykładowe dane wyjściowe dla 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
źródło
Bardzo ciekawy scenariusz. Głosowano za. Czy to pokaż-zmiany czy znajdź-zmiany?
VonC
@VonC dzięki, poprawione użycie, powinno być wszędzie
Marcin Raczkowski
0

Poniżej znajduje się nieelegancka metoda brutalnej siły, ale spodziewam się, że powinna działać. Upewnij się, że najpierw zachowałeś wszelkie niezatwierdzone zmiany, ponieważ zmieni to gałąź, w której aktualnie jesteś.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done
bielenie
źródło
1
Ponieważ jeśli znasz zmianę, możesz użyćgit log -S
Seth Robertson
Jaką powłokę zakładasz? Grzmotnąć?
Peter Mortensen
Żadnych bashizmów, które powinny działać na POSIX. Nie jestem pewien, czy for-each-refnadal przestrzega tej shortflagi. To powiedziawszy, zignoruj ​​moją odpowiedź; inne odpowiedzi są lepsze.
wybielenie