webentwicklung-frage-antwort-db.com.de

Zusammenführen, Aktualisieren und Ziehen von Git-Verzweigungen ohne Verwendung von Checkout

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?

559
charles

Die kurze Antwort

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.

Die lange Antwort

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:

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

  2. git fetch upstream master:master: Schnellvorlauf Ihres lokalen master an dieselbe Stelle wie upstream/master.

  3. git checkout - Checkt Ihren zuvor ausgecheckten Zweig aus (dies ist in diesem Fall mit - Der Fall).

Die Syntax von git fetch Für (Nicht-) Schnellvorlaufzusammenführungen

Wenn 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>

Die Dokumentation

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.

Siehe auch

  1. Auschecken und Zusammenführen von Git, ohne den Arbeitsbaum zu berühren

  2. Zusammenführen ohne Ändern des Arbeitsverzeichnisses

873
user456814

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.

80
Amber

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.

29
Cascabel

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

19
Cascabel

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

10
kkoehne

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.

10
Bennett McElwee

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.

6
wnoise

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

3
Casebash

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.

3
lkraider

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

Fügen Sie zum Einrichten Folgendes zu .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} 
}

Verwendungszweck:

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

2
rkd

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
0
Benjamin Atkin

nur um den Master zu ziehen, ohne den von mir verwendeten Master auszuchecken

git fetch Origin master:master

0
Sodhi saab
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>
0
grego