Как перебрать все ветки git, используя скрипт bash
Как я могу перебрать все локальные ветки в моем репозитории, используя скрипт bash. Мне нужно повторить и проверить, есть ли разница между веткой и некоторыми удаленными ветвями. бывший
for branch in $(git branch);
do
git log --oneline $branch ^remotes/origin/master;
done
Мне нужно сделать что-то, как указано выше, но проблема, с которой я сталкиваюсь, это $(ветка git), которая дает мне папки внутри папки репозитория вместе с ветками, присутствующими в репозитории.
Это правильный способ решить эту проблему? Или есть другой способ сделать это?
Спасибо
18 ответов
Вы не должны использовать git branch при написании скриптов. Git предоставляет "соединительный" интерфейс, который явно разработан для использования в сценариях (многие текущие и исторические реализации обычных команд Git (добавление, извлечение, объединение и т. Д.) Используют этот же интерфейс).
Нужная вам сантехническая команда: git for-each-ref:
git for-each-ref --shell \
--format='git log --oneline %(refname) ^origin/master' \
refs/heads/
Примечание: вам не нужно remotes/
префикс на удаленном реф, если у вас нет других реф, которые вызывают origin/master
чтобы найти несколько мест в пути поиска имени ссылки (см. "Символическое имя ссылки. …" в разделе "Задание версий" git-rev-parse (1)). Если вы пытаетесь явно избежать двусмысленности, используйте полное имя ссылки: refs/remotes/origin/master
,
Вы получите вывод, как это:
git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master
Вы можете передать этот вывод в sh.
Если вам не нравится идея генерации кода оболочки, вы можете отказаться от некоторой надежности * и сделать это:
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
git log --oneline "$branch" ^origin/master
done
* Имена ссылок должны быть защищены от разбиения слов в оболочке (см. Git-check-ref-format (1)). Лично я бы придерживался прежней версии (сгенерированный код оболочки); Я уверен, что с этим ничего не может быть неуместным.
Поскольку вы указали bash и он поддерживает массивы, вы можете поддерживать безопасность и при этом избегать генерации кишок вашего цикла:
branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
# …
done
Вы могли бы сделать что-то подобное с $@
если вы не используете оболочку, которая поддерживает массивы (set --
инициализировать и set -- "$@" %(refname)
добавить элементы).
Это потому что git branch
помечает текущую ветку звездочкой, например:
$ git branch
* master
mybranch
$
так $(git branch)
расширяется, например, до * master mybranch
а затем *
расширяется до списка файлов в текущем каталоге.
Я не вижу очевидной возможности не печатать звездочку в первую очередь; но вы можете отрубить это:
$(git branch | cut -c 3-)
git branch --format='%(refname:short)'
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
...
done
При этом используются команды git сантехники, предназначенные для написания сценариев. Это тоже просто и стандартно.
Ссылка: завершение Git Bash
Я повторяю как это например:
for BRANCH in `git branch --list|sed 's/\*//g'`;
do
git checkout $BRANCH
git fetch
git branch --set-upstream-to=origin/$BRANCH $BRANCH
done
git checkout master;
Самый простой вариант для запоминания на мой взгляд:
git branch | grep "[^* ]+" -Eo
Выход:
bamboo
develop
master
Параметр -o Grep (--only-Matching) ограничивает вывод только соответствующими частями ввода.
Поскольку ни пробел, ни * недопустимы в именах веток Git, возвращается список ветвей без лишних символов.
Редактировать: если вы находитесь в состоянии "отдельная голова", вам нужно отфильтровать текущую запись:
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
Будь проще
Простой способ получить имя ветки в цикле с помощью сценария bash.
#!/bin/bash
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
echo "${branch/'refs/heads/'/''}"
done
Выход:
master
other
Наконец-то разобрался, как сделать вывод без звездочки и без магии в аргументах:
$ git branch --format="%(refname:short)"
Почему это ценно?git branch
команда имеет некоторые дополнительные фильтры, такие как--merged
, что непросто реализовать с помощьюgit for-each-ref
(по крайней мере, насколько я вижу).
Принятый ответ является правильным и действительно должен быть использован подход, но решение проблемы в bash - отличное упражнение в понимании работы оболочек. Хитрость в том, чтобы сделать это с помощью bash без выполнения дополнительных манипуляций с текстом, состоит в том, чтобы гарантировать, что выходные данные ветви git никогда не будут расширены как часть команды, выполняемой оболочкой. Это предотвращает расширение звездочки в расширении имени файла (шаг 8) расширения оболочки (см. http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html).
Используйте конструкцию bash while с командой read, чтобы разделить вывод ветки git на строки. '*' Будет читаться как буквальный символ. Используйте оператор case, чтобы сопоставить его, обращая особое внимание на соответствующие шаблоны.
git branch | while read line ; do
case $line in
\*\ *) branch=${line#\*\ } ;; # match the current branch
*) branch=$line ;; # match all the other branches
esac
git log --oneline $branch ^remotes/origin/master
done
Звездочки как в конструкции случая bash, так и в подстановке параметров должны быть экранированы с помощью обратной косой черты, чтобы оболочка не интерпретировала их как символы сопоставления с образцом. Пробелы также экранированы (для предотвращения токенизации), потому что вы буквально совпадаете с '* '.
Я бы предложил $(git branch|grep -o "[0-9A-Za-z]\+")
если ваши местные филиалы названы только цифрами, буквами az и / или AZ
Ответ Googlian, но без использования for
git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
То, что я в итоге сделал, применил к вашему вопросу (и вдохновлен упоминанием ccpizza tr
):
git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done
(Я часто использую циклы while. Хотя для определенных вещей вы определенно захотите использовать указанную переменную с именем [например, "branch"), большую часть времени я занимаюсь только тем, чтобы что-то делать с каждой строкой ввода. "линия" здесь, а не "ветвь" - это признак многократного использования / мышечной памяти / эффективности.)
Если вы в этом состоянии:
git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/branch1
remotes/origin/branch2
remotes/origin/branch3
remotes/origin/master
И вы запускаете этот код:
git branch -a | grep remotes/origin/*
for BRANCH in `git branch -a | grep remotes/origin/*` ;
do
A="$(cut -d'/' -f3 <<<"$BRANCH")"
echo $A
done
Вы получите этот результат:
branch1
branch2
branch3
master
Правильный способ просто перебирать имена локальных веток - использоватьfor-each-ref
надrefs/heads/
. Например:
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do
echo branch="${branch}"
done
Это работает со стандартными значениями/значениями по умолчанию дляIFS
потому что пробелы недопустимы в именах веток в git.
Продолжая ответ @finn (спасибо!), Следующее позволит вам перебирать ветви без создания промежуточного сценария оболочки. Это достаточно надежно, если в названии ветки нет новых строк:)
git for-each-ref --format='%(refname)' refs/heads | while read x ; do echo === $x === ; done
Цикл while работает в подоболочке, что обычно нормально, если вы не устанавливаете переменные оболочки, к которым вы хотите обращаться в текущей оболочке. В этом случае вы используете процесс подстановки, чтобы перевернуть канал:
while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
Список заголовков (веток) в локальном репозитории
git show-ref --heads
git show-ref
- Список ссылок в локальном репозитории--heads
только списокheads
(а не теги)
Это перечислит головы примерно так
682e47c01dc8d0f4e4102f183190a48aaf34a3f0 refs/heads/main
....
поэтому, если вас интересует только имя, вы можете использовать что-то вроде
sed
чтобы получить желаемый результат
git show-ref --heads | sed 's/.*refs\/heads\///'
Перебирать ветки
С помощью этого вывода вы можете легко перебирать его, скажем, используя цикл bash, xargs, все, что плавает ваша лодка.
for SHA in $(git show-ref --heads | awk '{ print $1 }'); do
echo "magic! $SHA"
done
-
git show-ref --heads
получить ветки, как указано выше -
awk '{ print $1 }'
получить SHA -
echo "magic! $SHA"
<- здесь ты творишь свою магию
Конечно, теоретически нужно использовать специальный интерфейс, который Git действительно имеет для использования при написании сценариев. Но часто хочется чего- то попроще — удобно для анлайнера. Что-то, что не заставляет вас помнить такие вещи, как git for-each-ref --format… refs… аминь. И в любом случае это UNIX. Тогда это происходит так:
- Существует утилита, широко известная своим непонятным, но лаконичным способом вывода последнего столбца.
-
git branch
ставит звездочку перед названием ветки. Это означает, что нас, по-видимому, всегда интересует именно последний столбец.
Результат:
git branch | awk '{print $NF}'
#/bin/bash
for branch in $(git branch -r);
do
echo $branch
done