Trova un ramo Git contenente le modifiche a un determinato file


92

Ho 57 filiali locali. So di aver apportato una modifica a un determinato file in uno di essi, ma non sono sicuro di quale. C'è qualche tipo di comando che posso eseguire per trovare quali rami contengono modifiche a un determinato file?

Risposte:


90

Trova tutti i rami che contengono una modifica a FILENAME (anche se prima del punto di diramazione (non registrato))

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

Ispezionare manualmente:

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

Trova tutte le modifiche a FILENAME non unite allo schema:

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

Devo sostituire qualcosa in questo comando oltre a FILENAME? Restituisce tutti i 57 nomi di ramo.
Dustin

@Dustin: se tutti i 57 rami contengono quel nome file, tutti i 57 rami contengono una modifica che include quel nome file. I rami iniziano dalla revisione più vecchia raggiungibile su quel ramo, anche se quella revisione esisteva prima della creazione del ramo (il ramo è in alcuni sensi creato retroattivamente). Penso che tu debba definire meglio il tuo problema. Sai qual è il cambiamento? Potresti usare git log -Schange …o git log --grep LOGMESSAGE …(con ... che rappresenta il resto del comando che ho menzionato).
Seth Robertson

2
@Dustin: un'altra opzione è quella di utilizzare gitk --all -- filenameche ti mostrerà graficamente tutte le modifiche a quel file. Se riesci a identificare il commit in questione, puoi usarlo git branch --containsper vedere in quali rami è migrato il commit. Se vuoi vedere su quale ramo è stato originariamente creato il commit in questione , allora google git-what-branch, ma tieni presente che le unioni in avanti veloce possono oscurare tali informazioni.
Seth Robertson

@Seth: non riuscivo a ricordare il messaggio di commit o la modifica esatta del codice. Ho solo un'idea generale. Il problema è che non è ancora unito a master, quindi per usare gitk dovrei comunque controllare ogni ramo per trovare quello che sto cercando. Sarebbe bello se potessi dire "git, mostrami i rami che hanno apportato modifiche a FILENAME dal suo punto di diramazione"
Dustin

1
Potresti scrivere la prima riga in modo più efficientegit log --all --format='--contains %H' "$file" | xargs git branch
kojiro

53

Tutto ciò che serve è

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

Se vuoi conoscere subito la filiale puoi utilizzare anche:

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

Inoltre, se hai rinominato, potresti volerlo includere --followper il comando Git log.


16
Questo mostrerà i commit, ma ha chiesto quale ramo. Aggiungi un --sourcelì e sei d'oro.
Karl Bielefeldt

Grazie, ma non credo che mostri tutti i rami per ogni commit. Ma aiuterebbe.
Adam Dymitruk

1
Vero, ma in questo caso particolare ne cercava solo uno.
Karl Bielefeldt

btw per il percorso asp.net dovrebbe essere in un formato come AppName / Controllers / XController.cs
Ali Karaca

8

Sembra che questo sia ancora un problema senza una soluzione adeguata. Non ho abbastanza crediti per commentare, quindi ecco il mio piccolo contributo.

La prima soluzione di Seth Robertson ha funzionato per me, ma mi ha dato solo rami locali, tra i quali molti falsi positivi, probabilmente a causa delle fusioni dal ramo stabile.

La seconda soluzione di Adam Dymitruk non ha funzionato affatto per me. Per cominciare, cos'è --format =% 5? Non è riconosciuto da git, non sono riuscito a trovare nulla al riguardo e non sono riuscito a farlo funzionare con altre opzioni di formato.

Ma la sua prima soluzione combinata con l'opzione --source e con un semplice grep si è dimostrata utile:

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

Questo mi dà un numero di tag e rami remoti e un ramo locale, dove ho apportato le ultime modifiche al file. Non sono sicuro di quanto sia completo.

AGGIORNAMENTO secondo la richiesta di @nealmcb, ordinando i rami in base alla modifica più recente:

Per prima cosa, potresti cambiare grep in "refs / heads /.*", che ti darà solo i branch locali. Se ci sono solo pochi rami, puoi esaminare l'ultimo commit di ognuno in questo modo:

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

Se ci sono più rami e vuoi davvero automatizzarli, puoi combinare i due comandi usando xargs, git log formatting e un altro ordinamento in questo one-liner:

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

Ciò si tradurrà in un output come questo:

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

Ottimo grazie. Ora che ne dici di ordinare in base alla modifica più recente?
nealmcb

1

So che questa domanda è antica, ma ho continuato a tornarci prima di sviluppare la mia soluzione. Penso che sia più elegante e grazie all'uso della base di unione filtra i rami indesiderati.

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

Se lo metti in PATH come git-find-changes(con autorizzazioni eseguibili), puoi chiamarlo congit find-changes /path

Output di esempio per 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(+)

Sceneggiatura molto interessante. Votato. Si tratta di modifiche alla visualizzazione o alla ricerca di modifiche?
VonC

@VonC grazie, utilizzo corretto, dovrebbe essere trovato ovunque
Marcin Raczkowski

0

Quello che segue è un metodo di forza bruta poco elegante, ma mi aspetto che dovrebbe funzionare. Assicurati di aver prima nascosto tutte le modifiche non salvate poiché cambierà il ramo in cui ti trovi attualmente.

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

1
Perché se conosci il cambiamento puoi usaregit log -S
Seth Robertson

Quale shell stai assumendo? Bash?
Peter Mortensen

Nessun bashismo, dovrebbe funzionare su POSIX. Non sono sicuro che for-each-refrispetti ancora quella shortbandiera però. Detto questo, ignora la mia risposta; le altre risposte sono migliori.
Whiteinge
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.