Ich arbeite an einem Projekt mit zwei Zweigen, A und B. Normalerweise arbeite ich an Zweig A und füge Material aus Zweig B zusammen. Für das Zusammenführen würde ich normalerweise Folgendes tun:
git merge Origin/branchB
Ich möchte jedoch auch eine lokale Kopie von Zweig B behalten, da ich gelegentlich den Zweig auschecken kann, ohne ihn zuerst mit Zweig A zusammenzuführen. Dazu würde ich Folgendes tun:
git checkout branchB
git pull
git checkout branchA
Gibt es eine Möglichkeit, das oben genannte in einem Befehl auszuführen, ohne den Zweig hin und her schalten zu müssen? Sollte ich dafür git update-ref
Verwenden? Wie?
Solange Sie einen Schnellvorlauf durchführen, können Sie einfach verwenden
git fetch <remote> <sourceBranch>:<destinationBranch>
Beispiele:
# Merge local branch foo into local branch master,
# without having to checkout master first.
# Here `.` means to use the local repository as the "remote":
git fetch . foo:master
# Merge remote branch Origin/foo into local branch foo,
# without having to checkout foo first:
git fetch Origin foo:foo
Während Bernsteins Antwort auch im Schnellvorlauf funktioniert, ist die Verwendung von git fetch
Auf diese Weise ein wenig sicherer als das bloße Verschieben der Zweigreferenz, da git fetch
verhindert automatisch unbeabsichtigtes Vorspulen, solange Sie nicht +
in der Refspezifikation verwenden.
Sie können einen Zweig B nicht mit Zweig A zusammenführen, ohne zuerst A ausgecheckt zu haben, wenn dies zu einer Zusammenführung ohne schnellen Vorlauf führen würde. Dies liegt daran, dass eine Arbeitskopie erforderlich ist, um potenzielle Konflikte zu lösen.
Dies ist jedoch bei Schnellvorlauf-Zusammenführungen möglich , da solche Zusammenführungen definitionsgemäß niemals zu Konflikten führen können. Um dies zu tun, ohne zuerst einen Zweig auszuchecken, können Sie git fetch
Mit einer Referenzangabe verwenden.
Hier ist ein Beispiel für die Aktualisierung von master
(Änderungen, die nicht im Schnellvorlauf erfolgen, sind nicht zulässig), wenn ein anderer Zweig feature
ausgecheckt ist:
git fetch upstream master:master
Dieser Anwendungsfall ist so verbreitet, dass Sie wahrscheinlich einen Alias für ihn in Ihrer Git-Konfigurationsdatei erstellen möchten, wie dieser:
[alias]
sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
Dieser Alias bewirkt Folgendes:
git checkout HEAD
: Dies versetzt Ihre Arbeitskopie in einen Einzelkopfzustand. Dies ist nützlich, wenn Sie master
aktualisieren möchten, während Sie es ausgecheckt haben. Ich denke, das war notwendig, weil sich sonst die Verzweigungsreferenz für master
nicht verschieben lässt, aber ich erinnere mich nicht, ob das wirklich etwas ganz Besonderes ist.
git fetch upstream master:master
: Schnellvorlauf Ihres lokalen master
an dieselbe Stelle wie upstream/master
.
git checkout -
Checkt Ihren zuvor ausgecheckten Zweig aus (dies ist in diesem Fall mit -
Der Fall).
git fetch
Für (Nicht-) SchnellvorlaufzusammenführungenWenn der Befehl fetch
fehlschlagen soll, wenn die Aktualisierung nicht im Schnellvorlauf ausgeführt wird, verwenden Sie einfach eine Referenzspezifikation des Formulars
git fetch <remote> <remoteBranch>:<localBranch>
Wenn Sie Aktualisierungen ohne schnellen Vorlauf zulassen möchten, fügen Sie ein +
An der Vorderseite der Referenzspezifikation hinzu:
git fetch <remote> +<remoteBranch>:<localBranch>
Beachten Sie, dass Sie Ihr lokales Repo mit .
Als "remote" -Parameter übergeben können:
git fetch . <sourceBranch>:<destinationBranch>
Aus der git fetch
- Dokumentation, die diese Syntax erklärt (Hervorhebung meiner):
<refspec>
Das Format eines Parameters
<refspec>
Ist ein optionales Pluszeichen+
, Gefolgt von der Quellreferenz<src>
, Gefolgt von einem Doppelpunkt:
, Gefolgt von der Zielreferenz<dst>
.Die mit
<src>
Übereinstimmende ferne Referenz wird abgerufen, und wenn<dst>
Keine leere Zeichenfolge ist, wird die mit ihr übereinstimmende lokale Referenz vorgespult mit<src>
. Wenn das optionale Pluszeichen+
Verwendet wird, wird die lokale Referenz aktualisiert, auch wenn dies nicht zu einer Schnellvorlaufaktualisierung führt.
Nein, da ist kein. Ein Checkout des Zielzweigs ist erforderlich, damit Sie unter anderem Konflikte lösen können (wenn Git diese nicht automatisch zusammenführen kann).
Wenn es sich bei der Zusammenführung jedoch um eine Zusammenführung handelt, die im Schnelldurchlauf erfolgt, müssen Sie den Zielzweig nicht auschecken, da Sie eigentlich nichts zusammenführen müssen. Sie müssen lediglich den Zweig aktualisieren, um auf den zu verweisen neuer Kopf ref. Sie können dies mit git branch -f
Tun:
git branch -f branch-b branch-a
Aktualisiert branch-b
, Um auf den Kopf von branch-a
Zu verweisen.
Die Option -f
Steht für --force
, Was bedeutet, dass Sie bei der Verwendung vorsichtig sein müssen. Verwenden Sie es nur, wenn Sie sicher sind, dass die Zusammenführung im Schnellvorlauf erfolgt.
Wie Amber sagte, sind Schnellvorlauf-Zusammenführungen der einzige Fall, in dem Sie dies möglicherweise tun könnten. Jede andere Zusammenführung muss möglicherweise die gesamte dreifache Zusammenführung durchlaufen, Patches anwenden, Konflikte lösen - und das bedeutet, dass Dateien vorhanden sein müssen.
Ich habe zufällig ein Skript, das ich für genau das verwende: Schnellvorlauf-Zusammenführungen, ohne den Arbeitsbaum zu berühren (es sei denn, Sie verschmelzen mit HEAD). Es ist ein bisschen lang, weil es zumindest ein bisschen robust ist - es überprüft, ob die Zusammenführung ein schneller Vorlauf ist, und führt sie dann aus, ohne den Zweig auszuchecken, aber mit den gleichen Ergebnissen, als ob Sie es getan hätten - Sie sehen das diff --stat
Zusammenfassung der Änderungen, und der Eintrag im Reflog ist genau wie ein Schnellvorlauf, anstelle des "Zurücksetzens", das Sie erhalten, wenn Sie branch -f
Verwenden. Wenn Sie es git-merge-ff
Nennen und in Ihrem bin-Verzeichnis ablegen, können Sie es als git-Befehl aufrufen: git merge-ff
.
#!/bin/bash
_usage() {
echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
exit 1
}
_merge_ff() {
branch="$1"
commit="$2"
branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown branch $branch" 1>&2
_usage
fi
commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown revision $commit" 1>&2
_usage
fi
if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
git merge $quiet --ff-only "$commit"
else
if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
exit 1
fi
echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
if [ -z $quiet ]; then
echo "Fast forward"
git diff --stat "[email protected]{1}" "$branch"
fi
else
echo "Error: fast forward using update-ref failed" 1>&2
fi
fi
}
while getopts "q" opt; do
case $opt in
q ) quiet="-q";;
* ) ;;
esac
done
shift $((OPTIND-1))
case $# in
2 ) _merge_ff "$1" "$2";;
* ) _usage
esac
P.S. Wenn jemand Probleme mit diesem Skript sieht, kommentieren Sie bitte! Es war ein Schreib- und Vergessensjob, aber ich würde ihn gerne verbessern.
Sie können dies nur tun, wenn es sich bei der Zusammenführung um einen Schnellvorlauf handelt. Ist dies nicht der Fall, muss git die Dateien auschecken, damit sie zusammengeführt werden können!
Um es zu tun nur für einen schnellen Vorlauf:
git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
wo <commit>
ist das abgerufene Commit, zu dem Sie einen schnellen Vorlauf durchführen möchten. Dies ist im Grunde wie die Verwendung von git branch -f
, um den Zweig zu verschieben, es sei denn, er zeichnet ihn auch im Flog auf, als hätten Sie die Zusammenführung tatsächlich durchgeführt.
Bitte, bitte, bitte Tun Sie dies nicht für etwas, das kein schneller Vorlauf ist, oder Sie setzen einfach Ihren Zweig auf das andere Commit zurück. (Um zu überprüfen, ob git merge-base <branch> <commit>
gibt den SHA1 der Filiale an.)
Ein anderer, zugegebenermaßen ziemlich roher Weg ist, einfach den Zweig neu zu erstellen:
git fetch remote
git branch -f localbranch remote/remotebranch
Dies wirft die lokale veraltete Verzweigung weg und erstellt eine mit dem gleichen Namen neu. Gehen Sie also vorsichtig vor ...
In Ihrem Fall können Sie verwenden
git fetch Origin branchB:branchB
was macht was Sie wollen (vorausgesetzt, die Zusammenführung ist schnell vorwärts). Wenn die Verzweigung nicht aktualisiert werden kann, weil eine Zusammenführung ohne schnellen Vorlauf erforderlich ist, schlägt dies mit einer Nachricht sicher fehl.
Diese Art des Abrufs bietet noch weitere nützliche Optionen:
git fetch <remote> <sourceBranch>:<destinationBranch>
Beachten Sie, dass <remote>
kann ein lokales Repository sein und <sourceBranch>
kann ein Verfolgungszweig sein. So können Sie einen lokalen Zweig aktualisieren, auch wenn er nicht ausgecheckt ist ohne auf das Netzwerk zuzugreifen.
Derzeit erfolgt der Upstream-Serverzugriff über ein langsames VPN. Daher stelle ich regelmäßig eine Verbindung her. git fetch
, um alle Fernbedienungen zu aktualisieren, und trennen Sie dann die Verbindung. Wenn sich dann beispielsweise der Remote-Master geändert hat, kann ich das tun
git fetch . remotes/Origin/master:master
um meinen lokalen Master sicher auf den neuesten Stand zu bringen, auch wenn gerade eine andere Filiale ausgecheckt ist. Kein Netzwerkzugriff erforderlich.
Sie können das Repo klonen und das neue Repo zusammenführen. Auf demselben Dateisystem werden die meisten Daten nicht kopiert, sondern per Hardlink verknüpft. Zum Abschluss ziehen Sie die Ergebnisse in das ursprüngliche Repo.
In vielen Fällen (z. B. beim Zusammenführen) können Sie nur den Remote-Zweig verwenden, ohne den lokalen Verfolgungszweig aktualisieren zu müssen. Das Hinzufügen einer Nachricht in das Reflog klingt nach Overkill und verhindert, dass es schneller geht. Um die Wiederherstellung zu vereinfachen, fügen Sie Folgendes in Ihre Git-Konfiguration ein
[core]
logallrefupdates=true
Dann tippe
git reflog show mybranch
um die jüngste Geschichte Ihrer Branche zu sehen
Geben Sie Git-Forward-Merge ein:
Ohne das Ziel auschecken zu müssen,
git-forward-merge <source> <destination>
fügt die Quelle in den Zielzweig ein.
https://github.com/schuyler1d/git-forward-merge
Funktioniert nur für automatische Zusammenführungen, wenn Konflikte bestehen, die Sie für die regelmäßige Zusammenführung benötigen.
Ich habe eine Shell-Funktion für einen ähnlichen Anwendungsfall geschrieben, dem ich täglich bei Projekten begegne. Dies ist im Grunde eine Abkürzung, um lokale Niederlassungen auf dem neuesten Stand zu halten, beispielsweise mit der Funktion "Entwickeln", bevor Sie eine PR eröffnen.
Posten Sie dies, obwohl Sie
checkout
nicht verwenden möchten, falls andere diese Einschränkung nicht stören.
glmh
("git pull and merge here") wird automatisch checkout branchB
, pull
das Neueste, erneut checkout branchA
und merge branchB
.
Behebt nicht die Notwendigkeit, eine lokale Kopie von branchA zu behalten, kann jedoch problemlos geändert werden, indem vor dem Auschecken von branchB ein Schritt hinzugefügt wird. So etwas wie...
git branch ${branchA}-no-branchB ${branchA}
Bei einfachen Schnellvorläuferverbindungen wird zur Eingabeaufforderung für die Festschreibungsnachricht gesprungen.
Bei Zusammenführungen ohne Schnellvorlauf versetzt dies Ihre Zweigstelle in den Konfliktlösungsstatus (Sie müssen wahrscheinlich eingreifen).
.bashrc
Oder .zshrc
Hinzu:glmh() {
branchB=$1
[ $# -eq 0 ] && { branchB="develop" }
branchA="$(git branch | grep '*' | sed 's/* //g')"
git checkout ${branchB} && git pull
git checkout ${branchA} && git merge ${branchB}
}
# No argument given, will assume "develop"
> glmh
# Pass an argument to pull and merge a specific branch
> glmh your-other-branch
Hinweis: Dies ist nicht robust genug, um Argumente jenseits des Zweignamens an
git merge
Weiterzugeben.
Eine andere Möglichkeit, dies effektiv zu tun, ist:
git fetch
git branch -d branchB
git branch -t branchB Origin/branchB
Da es sich um einen Kleinbuchstaben -d
Handelt, wird er nur gelöscht, wenn die Daten noch irgendwo vorhanden sind. Es ist ähnlich wie @ kkoehnes Antwort, außer dass es nicht erzwingt. Aufgrund des -t
Wird die Fernbedienung erneut eingerichtet.
Ich hatte ein etwas anderes Bedürfnis als OP, nämlich nach dem Zusammenführen einer Pull-Anfrage einen neuen Feature-Zweig von develop
(oder master
) zu erstellen. Dies kann in einem Einzeiler ohne Kraftaufwand erreicht werden, aber der lokale Zweig develop
wird nicht aktualisiert. Es ist nur eine Frage des Auscheckens eines neuen Zweigs und dessen Basis Origin/develop
:
git checkout -b new-feature Origin/develop
nur um den Master zu ziehen, ohne den von mir verwendeten Master auszuchecken
git fetch Origin master:master
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
Du kannst es versuchen git worktree
zwei Äste nebeneinander offen zu haben, hört sich so an, als ob es das wäre, was du hast, aber sehr anders als einige der anderen Antworten, die ich hier gesehen habe.
Auf diese Weise können Sie zwei getrennte Zweige in demselben Git-Repo verfolgen, sodass Sie nur einmal abrufen müssen, um Aktualisierungen in beiden Arbeitsbäumen zu erhalten (anstatt zweimal git klonen und jeweils git ziehen zu müssen).
Worktree erstellt ein neues Arbeitsverzeichnis für Ihren Code, in dem Sie gleichzeitig einen anderen Zweig auschecken können, anstatt die Zweige auszutauschen.
Wenn Sie es entfernen möchten, können Sie mit bereinigen
git worktree remove [-f] <worktree>