Frage Wie man alle Git-Zweige mit Bash-Skript iteriert


Wie kann ich mit einem Bash-Skript alle lokalen Zweige in meinem Repository durchlaufen? Ich muss iterieren und prüfen, ob es irgendeinen Unterschied zwischen der Verzweigung und einigen entfernten Zweigen gibt. Ex

for branch in $(git branch); 
do
    git log --oneline $branch ^remotes/origin/master;
done

Ich muss etwas wie oben angegeben tun, aber das Problem, dem ich gegenüberstehe, ist $ (git branch), gibt mir die Ordner im Repository-Ordner zusammen mit den im Repository vorhandenen Zweigen.

Ist dies der richtige Weg, um dieses Problem zu lösen? Oder gibt es einen anderen Weg, es zu tun?

Vielen Dank


75
2017-10-02 15:33


Ursprung


Antworten:


Sie sollten nicht verwenden Git Zweig beim Schreiben von Skripten. Git bietet ein "Sanitär" Schnittstelle Dies ist explizit für die Verwendung in Skripten gedacht (viele aktuelle und historische Implementierungen normaler Git-Befehle (Hinzufügen, Auschecken, Zusammenführen usw.) verwenden dieselbe Schnittstelle).

Der gewünschte Installationsbefehl ist git für-jedes-ref:

git for-each-ref --shell \
  --format='git log --oneline %(refname) ^origin/master' \
  refs/heads/

Hinweis: Sie benötigen die remotes/ Präfix auf der Remote-Referenz, es sei denn, Sie haben andere refs, die verursachen origin/master um mehrere Orte im Referenznamen-Suchpfad zu finden (siehe "Ein symbolischer Referenzname. ..." in) der Abschnitt Überarbeitungen angeben von git-rev-parse (1)). Wenn du versuchst, Mehrdeutigkeiten explizit zu vermeiden, dann gehe mit dem vollständigen Ref-Namen: refs/remotes/origin/master.

Sie erhalten folgende Ausgabe:

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 diese Ausgabe in die Pipeline leiten Sch.

Wenn Sie die Idee, den Shell-Code zu generieren, nicht mögen, könnten Sie ein wenig Robustheit aufgeben* und tu dies:

for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    git log --oneline "$branch" ^origin/master
done

* Ref-Namen sollten vor der Worttrennung der Shell sicher sein (siehe Git-Check-Ref-Format (1)). Persönlich würde ich bei der früheren Version bleiben (generierter Shell-Code); Ich bin zuversichtlicher, dass damit nichts Unpassendes passieren kann.

Da du spezifiziert hast bash und es unterstützt Arrays, Sie können die Sicherheit erhalten und trotzdem vermeiden, den Kern Ihrer Schleife zu erzeugen:

branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
    # …
done

Du könntest etwas ähnliches mit machen $@ wenn Sie keine Shell verwenden, die Arrays unterstützt (set -- initialisieren und set -- "$@" %(refname) um Elemente hinzuzufügen).


123
2017-10-02 21:24



Das ist weil git branch markiert den aktuellen Zweig mit einem Stern, z.

$ git branch
* master
  mybranch
$ 

damit $(git branch) expandiert zu z.B. * master mybranchund dann die * wird auf die Liste der Dateien im aktuellen Verzeichnis erweitert.

Ich sehe keine offensichtliche Möglichkeit, den Stern überhaupt nicht auszudrucken; aber du könntest es abhacken:

$(git branch | cut -c 3-)

39
2017-10-02 15:49



ich würde vorschlagen $(git branch|grep -o "[0-9A-Za-z]\+") Wenn Ihre lokalen Niederlassungen nur durch Ziffern, a-z und / oder A-Z-Buchstaben benannt sind


4
2017-07-20 03:43



Die akzeptierte Antwort ist korrekt und sollte eigentlich der Ansatz sein, aber das Problem in bash zu lösen, ist eine großartige Übung, um zu verstehen, wie Shells funktionieren. Der Trick, dies mit bash zu tun, ohne zusätzliche Textmanipulationen durchzuführen, besteht darin, sicherzustellen, dass die Ausgabe des Git-Zweiges niemals als Teil eines Befehls ausgeführt wird, der von der Shell ausgeführt werden soll. Dies verhindert, dass der Stern in der Dateinamenerweiterung (Schritt 8) der Shell-Erweiterung jemals expandiert (siehe http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html)

Benutze die Bash während konstruiere mit einem Lesebefehl, um die git-Verzweigungsausgabe in Zeilen zu zerlegen. Das '*' wird als wörtliches Zeichen eingelesen. Verwenden Sie eine case-Anweisung, um sie zu vergleichen, wobei Sie besonders auf die übereinstimmenden Muster achten müssen.

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 der Bash Fall Konstrukt und in der Parameterersetzung muss mit Backslashes maskiert werden, um zu verhindern, dass die Shell sie als Mustervergleichszeichen interpretiert. Die Leerzeichen werden ebenfalls maskiert (um eine Tokenisierung zu verhindern), da Sie buchstäblich "*" zuordnen.


4
2018-01-30 08:32



Ich wiederhole es als 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;

4
2018-03-21 11:40



Die einfachste Möglichkeit, sich meiner Meinung nach zu erinnern:

git branch | grep "[^* ]+" -Eo

Ausgabe:

bamboo
develop
master

Die Option -o von Grep (--only-matching) beschränkt die Ausgabe nur auf die übereinstimmenden Teile der Eingabe.

Da in Git-Verzweigungsnamen weder space noch * gültig sind, wird die Verzweigungsliste ohne die zusätzlichen Zeichen zurückgegeben.

Bearbeiten: Wenn du drin bist "losgelöster Kopf" -Zustand, müssen Sie den aktuellen Eintrag herausfiltern:

git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE


1
2017-08-01 14:00



Ausgehend von @ Finns Antwort (Danke!) Können Sie im Folgenden über die Zweige iterieren, ohne ein intervenierendes Shell-Skript zu erstellen. Es ist robust genug, solange es keine Zeilenumbrüche im Zweignamen gibt :)

git for-each-ref --format='%(refname)' refs/heads  | while read x ; do echo === $x === ; done

Die while-Schleife wird in einer Subshell ausgeführt, was normalerweise gut ist, es sei denn, Sie setzen Shell-Variablen, auf die Sie in der aktuellen Shell zugreifen möchten. In diesem Fall verwenden Sie die Prozesssubstitution, um die Pipe umzukehren:

while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )

1
2017-10-23 22:31



Was ich getan habe, wurde auf deine Frage angewendet (und inspiriert von ccpizza, die erwähnt wurde) tr):

git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done

(Ich benutze While-Loops viel. Während für bestimmte Dinge, die Sie definitiv einen Spitznamen für die Variable verwenden möchten ["Branch", zum Beispiel], die meiste Zeit bin ich nur daran interessiert, etwas mit jedem zu tun Linie der Eingabe. Die Verwendung von "Linie" anstelle von "Verzweigung" ist ein Hinweis auf Wiederverwendbarkeit / Muskelgedächtnis / Effizienz.


1
2018-01-30 18:36