Wie durch alle git Zweige mit Bash-Skript iterieren
Frage
Wie kann ich durchlaufen alle lokalen Niederlassungen in meinem Repository mit Bash-Skript. Ich brauche zu durchlaufen und die Prüfung gibt es einen Unterschied zwischen der Branche und einigen abgelegenen Filialen. Ex
for branch in $(git branch);
do
git log --oneline $branch ^remotes/origin/master;
done
Ich brauche oben wie gegeben, etwas zu tun, aber das Problem, das ich mit Blick auf bin ist $ (git branch) gibt mir die Ordner im Repository-Ordnern zusammen mit den Zweigen präsentieren im Repository.
Ist dies der richtige Weg, um dieses Problem zu lösen? Oder gibt es eine andere Möglichkeit, es zu tun?
Danke
Lösung
Sie sollten verwenden nicht git branch , wenn Schreiben von Skripts. Git bietet einen „Sanitär“ Schnittstelle , die ist explizit für den Einsatz in scripting (vielen aktuellen und historischen Implementierungen von normalen Git-Befehlen (add, Kasse, merge, etc.) dieselbe Schnittstelle verwenden).
Die sanitären Anlagen Befehl Sie wollen, ist git for-each-ref :
git for-each-ref --shell \
--format='git log --oneline %(refname) ^origin/master' \
refs/heads/
Hinweis: Sie müssen nicht den remotes/
Präfix auf der Fernbedienung ref, wenn Sie andere Refs haben, dass Ursache origin/master
mehrere Orte in dem ref Suchpfad anzupassen (siehe unter die Spezifizierungs Revisions Abschnitt des GIT-rev-parse (1) ). Wenn Sie explictly versuchen, Unklarheiten zu vermeiden, dann mit der vollen ref gehen: refs/remotes/origin/master
.
Sie Ausgabe wie diese:
git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master
Sie können über die Pipeline diese Ausgabe in sh .
Wenn Sie die Erzeugung des Shell-Code nicht, wie die Idee zu tun, könnten Sie ein bisschen Robustheit aufgeben * und dies tun:
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
git log --oneline "$branch" ^origin/master
done
* Ref Namen sollten sicher sein, von der Shell-Wort Spaltung (siehe git-Check-ref-Format (1) ). Persönlich würde ich mit der früheren Version Stick (erzeugen Shell-Code); Ich bin zuversichtlich, dass nichts unangebracht damit passieren kann.
Da Sie angegeben bash und unterstützt Arrays, Sie Sicherheit behaupten können und vermeiden noch den Mut Ihrer Schleife zu erzeugen:
branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
# …
done
Sie könnten etwas ähnliches mit $@
tun, wenn Sie nicht eine Shell verwenden, dass Stützen Arrays (set --
zu initialisieren und set -- "$@" %(refname)
Elemente hinzuzufügen).
Andere Tipps
Das ist, weil git branch
markiert den aktuellen Zweig mit einem Stern, z.
$ git branch
* master
mybranch
$
so $(git branch)
ERWEITERT z.B. * master mybranch
, und dann werden die *
expandiert in die Liste der Dateien im aktuellen Verzeichnis.
Ich habe keine offensichtliche Option sehen Sie nicht den Druck der Stern an erster Stelle; aber man kann es abhacken:
$(git branch | cut -c 3-)
Die bash builtin, mapfile
, wird für diesen
alle git Zweige: git branch --all --format='%(refname:short)'
alle lokalen git Zweige: git branch --format='%(refname:short)'
alle Remote-git Zweige: git branch --remotes --format='%(refname:short)'
iterate durch alle git Zweige: mapfile -t -C my_callback -c 1 < <( get_branches )
Beispiel:
my_callback () {
INDEX=${1}
BRANCH=${2}
echo "${INDEX} ${BRANCH}"
}
get_branches () {
git branch --all --format='%(refname:short)'
}
# mapfile -t -C my_callback -c 1 BRANCHES < <( get_branches ) # if you want the branches that were sent to mapfile in a new array as well
# echo "${BRANCHES[@]}"
mapfile -t -C my_callback -c 1 < <( get_branches )
für die spezifische Situation der OP:
#!/usr/bin/env bash
_map () {
ARRAY=${1?}
CALLBACK=${2?}
mapfile -t -C "${CALLBACK}" -c 1 <<< "${ARRAY[@]}"
}
get_history_differences () {
REF1=${1?}
REF2=${2?}
shift
shift
git log --oneline "${REF1}" ^"${REF2}" "${@}"
}
has_different_history () {
REF1=${1?}
REF2=${2?}
HIST_DIFF=$( get_history_differences "${REF1}" "${REF2}" )
return $( test -n "${HIST_DIFF}" )
}
print_different_branches () {
read -r -a ARGS <<< "${@}"
LOCAL=${ARGS[-1]?}
for REMOTE in "${SOME_REMOTE_BRANCHES[@]}"; do
if has_different_history "${LOCAL}" "${REMOTE}"; then
# { echo; echo; get_history_differences "${LOCAL}" "${REMOTE}" --color=always; } # show differences
echo local branch "${LOCAL}" is different than remote branch "${REMOTE}";
fi
done
}
get_local_branches () {
git branch --format='%(refname:short)'
}
get_different_branches () {
_map "$( get_local_branches )" print_different_branches
}
# read -r -a SOME_REMOTE_BRANCHES <<< "${@}" # use this instead for command line input
declare -a SOME_REMOTE_BRANCHES
SOME_REMOTE_BRANCHES=( origin/master remotes/origin/another-branch another-remote/another-interesting-branch )
DIFFERENT_BRANCHES=$( get_different_branches )
echo "${DIFFERENT_BRANCHES}"
I Iterierte, wie es zum Beispiel:
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;
Ich würde vorschlagen
$(git branch|grep -o "[0-9A-Za-z]\+")
wenn Ihre lokalen Niederlassungen von Ziffern genannt werden, a-z und / oder A-Z Buchstaben nur
Die akzeptierte Antwort ist richtig und wirklich der Ansatz verwendet werden soll, aber das Problem in bash Lösung ist eine großartige Übung zu verstehen, wie Muscheln Arbeit. Der Trick zu tun dies mit bash ohne zusätzliche Textmanipulation durchgeführt wird, ist die Ausgabe von git Zweig, um sicherzustellen, nie als Teil eines Befehls erweitert wird von der Shell ausgeführt werden. Dies verhindert, dass der Stern von je im Dateinamen Expansion (Schritt 8) von Shell-Erweiterung seiner Erweiterung (siehe http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html )
Mit der bash während Konstrukt mit einem Lesebefehl der Verzweigungs git Ausgang in Linien zerhacken. Der ‚*‘ wird in als Literalzeichen gelesen werden. Verwenden Sie eine Case-Anweisung, es zu entsprechen, wobei besonderes Augenmerk auf die passenden Muster.
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
Die Sternchen in sowohl der bash Fall Konstrukt und im Parametersubstitution werden müssen, mit umgekehrten Schrägstrichen entkommen die Schale zu verhindern, dass sie als Musteranpassung Zeichen zu interpretieren. Die Räume sind auch entkam (tokenization zu verhindern), weil man buchstäblich passen ‚*‘.
Die einfachste Option meiner Meinung nach erinnern:
git branch | grep "[^* ]+" -Eo
Ausgabe:
bamboo
develop
master
grep -o Option (--only-Matching) begrenzt die Ausgabe auf nur die passenden Teile des Eingangs.
Da weder Raum noch * gültig sind in Git-Zweigs Namen, diese gibt die Liste der Filialen ohne die zusätzlichen Zeichen.
Edit: Wenn Sie in 'abgelöst Kopf' Zustand , müssen Sie aus dem aktuellen Eintrag zu Filter:
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
Was ich am Ende tun, angewandt auf Ihre Frage (und inspiriert von ccpizza erwähnen tr
):
git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done
(ich benutze While-Schleifen viel. Während für bestimmte Dinge würden Sie auf jeden Fall wollen einen spitzen Variablennamen verwenden, [ „Zweig“, zum Beispiel], die meisten der Zeit, die ich nur mit etwas zu tun, besorgt bin mit jede Zeile der Eingabe. Mit 'line' hier statt 'Zweigniederlassung' ist eine Anspielung auf Wiederverwertbarkeit / Muskelgedächtnis / Effizienz.)
Die Ausweitung auf von @ finn Antwort (danke!), Die folgende lassen Sie Iterierte über die Zweige ohne einen dazwischenliegenden Shell-Skript zu erstellen. Es ist robust genug, solange es keine Zeilenumbrüche in der Branche Namen:)
git for-each-ref --format='%(refname)' refs/heads | while read x ; do echo === $x === ; done
Die while-Schleife läuft in einer Sub-Shell, die in der Regel in Ordnung ist, es sei denn du bist Einstellung Shell-Variablen, die Sie in der aktuellen Shell zugreifen möchten. In diesem Fall verwenden Sie Prozess Substitution das Rohr umgekehrt:
while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
Wenn Sie in diesem Zustand sind:
git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/branch1
remotes/origin/branch2
remotes/origin/branch3
remotes/origin/master
Und Sie diesen Code ausführen:
git branch -a | grep remotes/origin/*
for BRANCH in `git branch -a | grep remotes/origin/*` ;
do
A="$(cut -d'/' -f3 <<<"$BRANCH")"
echo $A
done
Sie werden dieses Ergebnis erhalten:
branch1
branch2
branch3
master
Keep it simple
Der einfache Weg, um Zweignamen in Schleife Bash-Skript verwendet wird.
#!/bin/bash
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
echo "${branch/'refs/heads/'/''}"
done
Ausgabe:
master
other
Googlian Antwort, jedoch ohne Verwendung von für
git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
...
done
Diese verwendet git Sanitär Befehle, die für Scripting ausgelegt sind. Es ist auch einfach und Standard.
Referenz: Git Bash Abschluss