Найдите ветку Git, содержащую изменения в заданном файле

88

У меня 57 местных филиалов. Я знаю, что внес изменения в определенный файл в одном из них, но не уверен, в каком именно. Есть ли какая-то команда, которую я могу запустить, чтобы найти, какие ветки содержат изменения в определенном файле?

Дастин
источник

Ответы:

87

Найдите все ветки, которые содержат изменение 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: Если все 57 ветвей содержат это имя файла, то все 57 ветвей содержат изменение, которое включает это имя файла. Ветви начинаются с самой старой ревизии, доступной в этой ветке, даже если эта ревизия существовала до того, как вы создали ветку (ветка в некотором смысле создана задним числом). Я думаю, вам нужно лучше определить вашу проблему. Вы знаете, что это за изменение? Не могли бы вы использовать git log -Schange …или git log --grep LOGMESSAGE …(с… представляющими остальную часть упомянутой мной команды).
Сет Робертсон
2
@Dustin: Другой вариант - использовать, gitk --all -- filenameкоторый графически покажет вам все изменения в этом файле. Если вы можете идентифицировать рассматриваемый коммит, вы можете использовать его, git branch --containsчтобы увидеть, в какие ветви он перешел. Если вы хотите увидеть, в какой ветке изначально был создан рассматриваемый коммит , тогда используйте google git-what-branch, но имейте в виду, что слияния с быстрой перемоткой вперед могут скрыть эту информацию.
Сет Робертсон
@Seth: я не мог вспомнить сообщение фиксации или точное изменение кода. У меня просто общее представление. Проблема в том, что он еще не объединен с мастером, поэтому для использования gitk мне все равно придется проверять каждую ветку, чтобы найти то, что я ищу. Было бы неплохо, если бы я мог сказать: «git, покажи мне ветки, которые внесли изменения в FILENAME с момента его точки ветвления»
Дастин
1
Вы могли бы написать первую строчку более эффективноgit log --all --format='--contains %H' "$file" | xargs git branch
Кодзиро
52

Все, что тебе нужно

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 log.

Адам Димитрук
источник
15
Это покажет коммиты, но он спросил, в какой ветке. Добавьте --sourceтуда, и вы золотой.
Karl Bielefeldt
Спасибо, но я не думаю, что это показывает все ветки для каждого коммита. Но это поможет.
Adam Dymitruk 06
1
Верно, но в данном случае он искал только одного.
Karl Bielefeldt
btw для пути asp.net должен быть в формате вроде AppName / Controllers / XController.cs
Али Карака
8

Похоже, это проблема без соответствующего решения. У меня недостаточно кредитов для комментариев, так что вот мой небольшой вклад.

1-е решение Сета Робертсона вроде сработало для меня, но дало мне только локальные ветки, среди которых было много ложных срабатываний, вероятно, из-за слияний из стабильной ветки.

Второе решение Адама Димитрука у меня вообще не сработало. Для начала, что --format =% 5? Он не распознается git, я ничего не мог найти об этом и не мог заставить его работать с другими параметрами формата.

Но его 1-е решение в сочетании с параметром --source и простым grep оказалось полезным:

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

Это дает мне несколько удаленных тегов и веток и одну локальную ветку, в которой я внес последние изменения в файл. Не уверен, насколько это полно.

ОБНОВЛЕНИЕ согласно запросу @nealmcb, сортировка веток по последнему изменению:

Во-первых, вы можете изменить grep на «refs / Heads /.*», что даст вам только локальные ветки. Если веток всего несколько, вы можете проверить последнюю фиксацию каждой из них следующим образом:

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

Если веток больше, и вы действительно хотите автоматизировать это, вы можете объединить две команды, используя xargs, форматирование журнала git и другую сортировку, в этот однострочник:

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
Simpleton
источник
Отлично, спасибо. А как насчет сортировки по последним изменениям?
nealmcb
1

Я знаю, что это древний вопрос, но я постоянно возвращался к нему, прежде чем разработать собственное решение. Я считаю, что это более элегантно и благодаря использованию базы слияния отфильтровывает нежелательные ветви.

#!/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, спасибо, исправленное использование, должно быть везде
Марцин Рачковски,
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
Сет Робертсон
Какую оболочку вы предполагаете? Баш?
Питер Мортенсен
Никаких башизмов, которые должны работать в POSIX. Я не уверен, что for-each-refдо сих пор уважает этот shortфлаг. Тем не менее, игнорируйте мой ответ; другие ответы лучше.
whiteinge 01