Rechercher une branche Git contenant des modifications dans un fichier donné

88

J'ai 57 succursales locales. Je sais que j'ai modifié un certain fichier dans l'un d'entre eux, mais je ne sais pas lequel. Existe-t-il une sorte de commande que je peux exécuter pour trouver quelles branches contiennent des modifications à un certain fichier?

Dustin
la source

Réponses:

87

Trouvez toutes les branches qui contiennent un changement de FILENAME (même si avant le point de branchement (non enregistré))

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

Inspectez manuellement:

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

Rechercher toutes les modifications apportées à FILENAME non fusionnées avec le masque:

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
la source
Dois-je remplacer quoi que ce soit dans cette commande en plus de FILENAME? Il renvoie les 57 noms de branche.
Dustin le
@Dustin: Si les 57 branches contiennent ce nom de fichier, alors toutes les 57 branches contiennent un changement qui inclut ce nom de fichier. Les branches commencent à partir de la révision la plus ancienne accessible sur cette branche, même si cette révision existait avant la création de la branche (la branche est dans certains sens créée rétroactivement). Je pense que vous devez mieux définir votre problème. Savez-vous quel est le changement? Pourriez-vous utiliser git log -Schange …ou git log --grep LOGMESSAGE …(avec… représentant le reste de la commande que j'ai mentionnée).
Seth Robertson le
2
@Dustin: Une autre option est d'utiliser gitk --all -- filenamece qui vous montrera graphiquement toutes les modifications apportées à ce fichier. Si vous pouvez identifier le commit en question, vous pouvez utiliser git branch --containspour voir vers quelles branches le commit a migré. Si vous voulez voir sur quelle branche le commit en question a été créé à l'origine, alors google git-what-branch, mais sachez que les fusions à avance rapide peuvent masquer ces informations.
Seth Robertson le
@Seth: Je ne me souvenais pas du message de validation ou du changement de code exact. J'ai juste une idée générale. Le problème est qu'il n'est pas encore fusionné avec master, donc pour utiliser gitk, je devrais quand même vérifier chaque branche pour trouver ce que je recherche. Ce serait bien si je pouvais dire, "git, montre-moi les branches qui ont apporté des modifications à FILENAME depuis son point de branchement"
Dustin
1
Vous pourriez écrire la première ligne plus efficacementgit log --all --format='--contains %H' "$file" | xargs git branch
kojiro
52

Tout ce dont tu as besoin c'est

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

Si vous souhaitez connaître la branche tout de suite, vous pouvez également utiliser:

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

De plus, si vous avez renommé, vous souhaiterez peut-être l'inclure --followpour la commande Git log.

Adam Dymitruk
la source
15
Cela montrera les commits, mais il a demandé quelle branche. Ajoutez-en un --sourceet vous êtes en or.
Karl Bielefeldt le
Merci, mais je ne pense pas que cela montre toutes les branches pour chaque commit. Mais cela aiderait.
Adam Dymitruk le
1
C'est vrai, mais dans ce cas particulier, il n'en cherchait qu'un.
Karl Bielefeldt
btw pour le chemin asp.net doit être au format AppName / Controllers / XController.cs
Ali Karaca
8

On dirait que c'est toujours un problème sans solution appropriée. Je n'ai pas assez de crédits pour commenter, alors voici ma petite contribution.

La première solution de Seth Robertson a un peu fonctionné pour moi, mais ne m'a donné que des branches locales, parmi lesquelles de nombreux faux positifs, probablement à cause de fusions de la branche stable.

La deuxième solution d'Adam Dymitruk n'a pas du tout fonctionné pour moi. Pour commencer, qu'est-ce que --format =% 5? Il n'est pas reconnu par git, je n'ai rien trouvé à ce sujet et je n'ai pas pu le faire fonctionner avec d'autres options de format.

Mais sa 1ère solution combinée avec l'option --source et avec un simple grep s'est avérée utile:

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

Cela me donne un certain nombre de balises et de branches distantes et une branche locale, où j'ai apporté les dernières modifications au fichier. Je ne sais pas à quel point c'est complet.

MISE À JOUR selon la demande de @nealmcb, en triant les branches par modification la plus récente:

Tout d'abord, vous pouvez changer le grep en "refs / heads /.*", ce qui vous donnera uniquement les branches locales. S'il n'y a que quelques branches, vous pouvez examiner le dernier commit de chacune comme ceci:

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

S'il y a plus de branches et que vous voulez vraiment automatiser cela, vous pouvez combiner les deux commandes en utilisant xargs, le formatage git log et un autre tri dans ce 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

Cela se traduira par une sortie comme celle-ci:

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
Niais
la source
Super merci. Maintenant, que diriez-vous de trier par changement le plus récent?
nealmcb
1

Je sais que cette question est ancienne, mais j'y revenais sans cesse avant de développer ma propre solution. Je pense que c'est plus élégant et grâce à l'utilisation de la base de fusion filtre les branches indésirables.

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

Si vous le mettez dans PATH comme git-find-changes(avec des autorisations exécutables), vous pouvez alors l'appeler avecgit find-changes /path

Exemple de sortie pour 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
la source
Script très intéressant. J'ai voté pour. S'agit-il de show-changes ou de find-changes?
VonC
@VonC merci, utilisation corrigée, devrait être trouvée partout
Marcin Raczkowski
0

Ce qui suit est une méthode de force brute inélégante, mais je pense que cela devrait fonctionner. Assurez-vous que vous avez d'abord caché toutes les modifications non validées, car cela changera la branche sur laquelle vous vous trouvez actuellement.

for branch in $(git for-each-ref --format="%(refname:short)" refs/heads); do
    git checkout $branch && git grep SOMETHING
done
whiteinge
la source
1
Parce que si vous connaissez le changement, vous pouvez utilisergit log -S
Seth Robertson
Quelle coquille supposez-vous? Frapper?
Peter Mortensen
Pas de bashismes, cela devrait fonctionner sur POSIX. Je ne suis pas sûr que cela for-each-refrespecte toujours ce shortdrapeau. Cela dit, ignorez ma réponse; les autres réponses sont meilleures.
whiteinge