Ist git-svn dcommit nach dem Zusammenführen in git gefährlich?

133

Meine Motivation, git-svn auszuprobieren, ist das mühelose Zusammenführen und Verzweigen. Dann bemerkte ich, dass Mann git-svn (1) sagt:

Das Ausführen von Git-Merge oder Git-Pull wird NICHT für einen Zweig empfohlen, von dem aus Sie einen Commit durchführen möchten. Subversion stellt keine Zusammenführungen in angemessener oder nützlicher Weise dar; Daher können Benutzer, die Subversion verwenden, keine von Ihnen vorgenommenen Zusammenführungen sehen. Wenn Sie einen Git-Zweig zusammenführen oder daraus ziehen, der ein Spiegel eines SVN-Zweigs ist, wird dcommit möglicherweise auf den falschen Zweig festgeschrieben.

Bedeutet dies, dass ich keinen lokalen Zweig aus svn / trunk (oder einem Zweig) erstellen, weghacken, wieder in svn / trunk zusammenführen und dann dcommit kann? Ich verstehe, dass SVN-Benutzer das gleiche Durcheinander sehen werden, das in SVN vor 1.5.x zusammengeführt wurde, aber gibt es noch andere Nachteile? Dieser letzte Satz macht mir auch Sorgen. Tun Menschen solche Dinge routinemäßig?

Knut Eldhuset
quelle
14
Dies ist einfach falsch: Subversion stellt keine Zusammenführungen in angemessener oder nützlicher Weise dar; Daher können Benutzer, die Subversion verwenden, keine von Ihnen vorgenommenen Zusammenführungen sehen. Wenn Sie einen Git-Zweig zusammenführen oder daraus ziehen, der ein Spiegel eines SVN-Zweigs ist, wird dcommit möglicherweise auf den falschen Zweig festgeschrieben. Subversion könnte nicht nur Git Merge Tracking-Informationen darstellen, sondern auch viel feinkörnigere Informationen. Es ist git-svn, das nicht das richtige svn: mergeinfo erstellt oder dcommit für einen richtigen Zweig erstellt.
Alexander Kitaev
2
@AlexanderKitaev: Die git-svn-Dokumente haben sich seitdem geändert, und zusätzlich gibt es eine --mergeinfo=<mergeinfo>Option, mit der Zusammenführungsinformationen möglicherweise an den SVN übergeben werden können. Ich bin mir nicht sicher, wie es verwendet werden soll.
Mr_and_Mrs_D

Antworten:

174

Eigentlich habe ich mit der --no-ffOption zum Zusammenführen von Git einen noch besseren Weg gefunden . All diese Squash-Technik, die ich zuvor verwendet habe, ist nicht mehr erforderlich.

Mein neuer Workflow lautet jetzt wie folgt:

  • Ich habe einen „Master“ Zweig, der der einzige Zweig ist , dass ich aus und das Klon der SVN - Repository dcommit ( -svorausgesetzt , dass Sie haben ein Standard - SVN - Layout im Repository trunk/, branches/und tags/):

    git svn clone [-s] <svn-url>
    
  • Ich arbeite an einer lokalen Niederlassung "Arbeit" ( -berstellt die Niederlassung "Arbeit")

    git checkout -b work
    
  • Commit lokal in den Zweig "work" ( -sum Ihre Commit-Nachricht abzumelden). In der Folge gehe ich davon aus, dass Sie 3 lokale Commits gemacht haben

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

Jetzt möchten Sie sich auf den SVN-Server festlegen

  • Verstecken Sie [eventuell] die Änderungen, die auf dem SVN-Server nicht festgeschrieben werden sollen (häufig haben Sie Code in der Hauptdatei kommentiert, nur weil Sie die Kompilierung beschleunigen und sich auf eine bestimmte Funktion konzentrieren möchten).

    (work)$> git stash
    
  • Basis des Hauptzweigs mit dem SVN-Repository neu erstellen (zum Aktualisieren vom SVN-Server)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • Gehen Sie zurück zum Arbeitszweig und starten Sie den Master erneut

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Stellen Sie sicher, dass alles in Ordnung ist, zum Beispiel:

    (work)$> git log --graph --oneline --decorate
    
  • Jetzt ist es an der Zeit, alle drei Commits aus dem Zweig "work" mit dieser wunderbaren --no-ffOption in "master" zusammenzuführen

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • Sie können den Status der Protokolle feststellen:

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • Jetzt möchten Sie wahrscheinlich amenddas letzte Commit für Ihre SVN-Typen bearbeiten ( ) (andernfalls wird nur ein einziges Commit mit der Meldung "Zweig 'Arbeit' zusammenführen" angezeigt.

    (master)$> git commit --amend
    
  • Zum Schluss auf dem SVN-Server festschreiben

    (master)$> git svn dcommit
    
  • Machen Sie sich wieder an die Arbeit und stellen Sie schließlich Ihre versteckten Dateien wieder her:

    (master)$> git checkout work
    (work)$> git stash pop
    
Sebastien Varrette
quelle
4
Dies ist genau der Workflow, nach dem dieser Git N00B gesucht hat. Erstellen Sie einen lokalen Zweig, um einen Fehler zu beheben, tun Sie alles, und senden Sie ihn dann mit einem SINGLE-Commit an svn. Die Verwendung der am höchsten bewerteten Antwort hier hat meine Arbeit durcheinander gebracht - ich konnte svn rebase nicht ohne Fehler durchführen.
João Bragança
19
Ist das nicht genau das, wovor die im op zitierten git-svn-Dokumente warnen? Durch Ausführen der Zusammenführung mit der --no-ffOption erstellen Sie explizit ein Zusammenführungs-Commit (ein Commit mit zwei übergeordneten Elementen) anstelle eines schnellen Vorlaufs. Um sicherzustellen, dass alle Commits im SVN-Tracking-Zweig Single-Parent-Commits sind, müssen alle Merges entweder schnell vorgespult werden ( --ff-onlykann dabei helfen) oder, wenn sich der Trunk hinter Ihrem Rücken geändert hat, a --squash, richtig?
Jemmons
4
Dies funktioniert für einen einmaligen Zweig, den Sie sofort löschen, aber git svn dcommitdas von Ihnen angegebene Git-Commit neu schreiben. Dies bedeutet, dass Sie das andere übergeordnete Element verlieren und Ihr Git-Repo jetzt keinen Datensatz mehr hat, in den Sie diesen Zweig jemals zusammengeführt haben master. Dies kann auch dazu führen, dass Ihr Arbeitsverzeichnis in einem inkonsistenten Zustand bleibt.
Rescdsk
9
Verwenden Sie git merge --no-ff work -m "commit message"anstelle eines zusätzlichen git commit --amendSchrittes
Tekumara
3
Für mich würde ich lieber "git merge --ff-only work" verwenden, da ich alle meine Commits und nicht nur den letzten
beibehalten möchte
49

Das Erstellen lokaler Filialen ist mit git-svn definitiv möglich. Solange Sie nur lokale Zweige für sich selbst verwenden und nicht versuchen, git zum Zusammenführen zwischen vorgelagerten SVN-Zweigen zu verwenden, sollte es Ihnen gut gehen.

Ich habe einen "Master" -Zweig, mit dem ich den SVN-Server verfolge. Dies ist der einzige Zweig, aus dem ich mich verpflichte. Wenn ich etwas arbeite, erstelle ich einen Themenzweig und arbeite daran. Wenn ich es festschreiben möchte, mache ich Folgendes:

  1. Übertragen Sie alles in den Themenbereich
  2. git svn rebase ( Löse alle Konflikte zwischen deiner Arbeit und svn)
  3. Git Checkout Master
  4. git svn rebase (dies macht den nächsten Schritt zu einem schnellen Vorlauf, siehe Aarons Kommentare unten)
  5. git merge topic_branch
  6. Lösen Sie Zusammenführungskonflikte (an dieser Stelle sollten keine vorhanden sein).
  7. git svn dcommit

Ich habe auch eine andere Situation, in der ich einige lokale Änderungen (zum Debuggen) beibehalten muss, die niemals auf svn übertragen werden sollten. Dafür habe ich den oben genannten Hauptzweig, aber auch einen Zweig namens "Arbeit", in dem ich normalerweise arbeite. Themenzweige werden von der Arbeit abgezweigt. Wenn ich dort Arbeit festschreiben möchte, checke ich den Master aus und verwende Cherry-Pick, um die Festschreibungen aus dem Arbeitszweig auszuwählen, den ich für svn festschreiben möchte. Dies liegt daran, dass ich vermeiden möchte, die drei lokalen Änderungs-Commits festzuschreiben. Dann verlasse ich mich von der Hauptniederlassung und stütze alles neu.

Es lohnt sich, git svn dcommit -nzuerst zu laufen , um sicherzustellen, dass Sie genau das festlegen, was Sie festlegen möchten. Im Gegensatz zu Git ist es schwierig, die Geschichte in SVN neu zu schreiben!

Ich bin der Meinung, dass es einen besseren Weg geben muss, die Änderung in einem Themenzweig zusammenzuführen, während diese lokalen Änderungszusagen übersprungen werden, als Cherry Pick zu verwenden. Wenn also jemand Ideen hat, wäre er willkommen.

Greg Hewgill
quelle
Wenn ich das richtig verstehe, ist es in git in git, git mit svn zusammenzuführen, aber nicht svn mit svn? Also kann ich meinen svn / branch nicht mit svn / trunk in git zusammenführen?
Knut Eldhuset
1
Das Umschreiben des Verlaufs in SVN ist schwierig, wenn Sie etwas Triviales tun möchten. In nicht trivialen Fällen ist dies praktisch unmöglich.
Aristoteles Pagaltzis
19
Greg Hewgills Antwort hat viele Stimmen, aber ich glaube, es ist falsch. Das Zusammenführen von topic_branch in master ist nur dann sicher, wenn es sich nur um eine schnelle Zusammenführung handelt. Wenn es sich um eine echte Zusammenführung handelt, für die ein Zusammenführungs-Commit erforderlich ist, geht das Zusammenführungs-Commit verloren, wenn Sie ein Commit durchführen. Wenn Sie das nächste Mal versuchen, topic_branch in master zusammenzuführen, glaubt git, dass die erste Zusammenführung nie stattgefunden hat, und die Hölle bricht los. Siehe die Frage Warum verliert git svn dcommit die Historie der Zusammenführungs-Commits für lokale Niederlassungen?
Aaron
1
@ Greg Hewgill, die Bearbeitung ist nicht klar. Sie haben geschrieben, dass die Rebase "das nächste Commit zu einem schnellen Vorlauf macht". Ich weiß nicht, was das bedeutet, da eine Schnellvorlaufzusammenführung kein Commit beinhaltet, aber vielleicht meinten Sie "macht die nächste Zusammenführung zu einer Schnellvorlaufzusammenführung". Aber wenn Sie das gemeint haben, ist es nicht richtig. Ob eine Zusammenführung von topic_branch zu master eine Schnellvorlaufzusammenführung ist oder nicht, hängt davon ab, ob seit dem Verzweigungspunkt Commits für master vorgenommen wurden. Wenn Sie den Master neu starten, werden neue Commits für den Master erstellt, sodass eine nachfolgende Zusammenführung nicht unbedingt eine schnelle Vorwärtszusammenführung ist.
Aaron
1
@ Aaron: Ja, ich weiß nicht, was ich dort gedacht habe. Ich habe es wieder behoben, hoffentlich macht das Sinn. Eines der Probleme ist, dass es in Git so viele Möglichkeiten gibt, Dinge zu tun, dass es einer Erklärung bedarf, um eine bestimmte Abfolge von Aktionen zu beschreiben .
Greg Hewgill
33

Einfache Lösung: Entfernen Sie nach dem Zusammenführen den Zweig "Arbeit"

Kurze Antwort: Sie können git verwenden, wie Sie möchten (siehe unten für einen einfachen Workflow), einschließlich Zusammenführen. Stellen Sie einfach sicher, dass Sie jeder ' Git Merge Work ' mit ' Git Branch -d Work ' folgen , um den temporären Arbeitszweig zu löschen.

Hintergrunderklärung: Das Problem beim Zusammenführen / Festschreiben besteht darin, dass der Zusammenführungsverlauf dieses Zweigs immer dann, wenn Sie einen Zweig "git svn dcommit" ausführen, "abgeflacht" wird: git vergisst alle Zusammenführungsvorgänge, die in diesen Zweig ausgeführt wurden: Nur der Dateiinhalt bleibt erhalten. Die Tatsache, dass dieser Inhalt (teilweise) aus einem bestimmten anderen Zweig stammt, geht jedoch verloren. Siehe: Warum verliert git svn dcommit die Historie der Zusammenführungs-Commits für lokale Niederlassungen?

(Hinweis: Es gibt nicht viel, was git-svn dagegen tun könnte: svn versteht die viel leistungsstärkeren Git-Zusammenführungen einfach nicht. Daher können diese Zusammenführungsinformationen im svn-Repository in keiner Weise dargestellt werden.)

Aber das ist das ganze Problem. Wenn Sie den Zweig 'work' löschen, nachdem er mit dem Zweig 'master' zusammengeführt wurde, ist Ihr Git-Repository zu 100% sauber und sieht genauso aus wie Ihr SVN-Repository.

Mein Workflow: Natürlich habe ich zuerst das Remote-SVN-Repository in ein lokales Git-Repository geklont (dies kann einige Zeit dauern):

$> git svn clone <svn-repository-url> <local-directory>

Alle Arbeiten finden dann im "lokalen Verzeichnis" statt. Wann immer ich Updates vom Server erhalten muss (wie 'svn update'), mache ich:

$> git checkout master
$> git svn rebase

Ich mache alle meine Entwicklungsarbeiten in einem separaten Zweig 'Arbeit', der wie folgt erstellt wird:

$> git checkout -b work

Natürlich können Sie so viele Zweige für Ihre Arbeit erstellen, wie Sie möchten, und sie zusammenführen und neu erstellen, wie Sie möchten (löschen Sie sie einfach, wenn Sie damit fertig sind - wie unten beschrieben). In meiner normalen Arbeit verpflichte ich mich sehr häufig:

$> git commit -am '-- finished a little piece of work'

Der nächste Schritt (git rebase -i) ist optional - es wird nur der Verlauf bereinigt, bevor er auf svn archiviert wird: Sobald ich einen stabilen Meilenstein erreicht habe, den ich mit anderen teilen möchte, schreibe ich den Verlauf dieser 'Arbeit' neu. Verzweigen und bereinigen Sie die Commit-Nachrichten (andere Entwickler müssen nicht alle kleinen Schritte und Fehler sehen, die ich unterwegs gemacht habe - nur das Ergebnis). Dafür mache ich

$> git log

und kopieren Sie den sha-1-Hash des letzten Commits, der im svn-Repository aktiv ist (wie durch eine git-svn-id angegeben). Dann rufe ich an

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Fügen Sie einfach sha-1 Hash unseres letzten svn Commits anstelle von meinem ein. Weitere Informationen finden Sie in der Dokumentation mit 'git help rebase'. Kurz gesagt: Dieser Befehl öffnet zuerst einen Editor, der Ihre Commits präsentiert ---- Ändern Sie einfach 'pick' in 'squash' für alle Commits, die Sie mit vorherigen Commits quetschen möchten. Natürlich sollte die erste Zeile als "Auswahl" bleiben. Auf diese Weise können Sie Ihre vielen kleinen Commits zu einer oder mehreren sinnvollen Einheiten zusammenfassen. Speichern Sie den Editor und beenden Sie ihn. Sie werden von einem anderen Editor aufgefordert, die Commit-Protokollnachrichten neu zu schreiben.

Kurz gesagt: Nachdem ich das 'Code-Hacken' beendet habe, massiere ich meinen 'Arbeits'-Zweig, bis er so aussieht, wie ich ihn den anderen Programmierern präsentieren möchte (oder wie ich die Arbeit in ein paar Wochen sehen möchte, wenn ich den Verlauf durchsuche). .

Um die Änderungen in das SVN-Repository zu übertragen, gehe ich wie folgt vor:

$> git checkout master
$> git svn rebase

Jetzt sind wir wieder im alten 'Master'-Zweig, der mit allen Änderungen aktualisiert wurde, die in der Zwischenzeit im svn-Repository vorgenommen wurden (Ihre neuen Änderungen sind im' Arbeits'-Zweig versteckt).

Wenn es Änderungen gibt, die mit Ihren neuen "Arbeits" -Änderungen in Konflikt geraten können, müssen Sie diese lokal auflösen, bevor Sie Ihre neue Arbeit verschieben können (siehe Details weiter unten). Dann können wir unsere Änderungen auf svn übertragen:

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

Hinweis 1: Der Befehl 'git branch -d work' ist ziemlich sicher: Er ermöglicht nur das Löschen von Zweigen, die Sie nicht mehr benötigen (da diese bereits in Ihrem aktuellen Zweig zusammengeführt sind). Wenn Sie diesen Befehl versehentlich ausführen, bevor Sie Ihre Arbeit mit dem Zweig 'master' zusammenführen, wird eine Fehlermeldung angezeigt.

Hinweis 2: Stellen Sie sicher, dass Sie Ihren Zweig mit 'git branch -d work' zwischen Zusammenführen und dcommit löschen: Wenn Sie versuchen, den Zweig nach dcommit zu löschen, wird eine Fehlermeldung angezeigt: Wenn Sie 'git svn dcommit' ausführen, vergisst git dies Ihr Zweig wurde mit 'master' zusammengeführt. Sie müssen es mit 'git branch -D work' entfernen, das die Sicherheitsüberprüfung nicht durchführt.

Jetzt erstelle ich sofort einen neuen 'Arbeits'-Zweig, um ein versehentliches Hacken des' Master'-Zweigs zu vermeiden:

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

Integrieren Ihrer 'Arbeit' in Änderungen an svn: Folgendes mache ich, wenn 'git svn rebase' zeigt, dass andere das svn-Repository geändert haben, während ich an meinem Zweig 'Arbeit' gearbeitet habe:

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

Es gibt leistungsfähigere Lösungen: Der vorgestellte Workflow ist simpel: Er nutzt die Möglichkeiten von git nur in jeder Runde von 'update / hack / dcommit' - lässt jedoch die langfristige Projekthistorie genauso linear wie das svn-Repository. Dies ist in Ordnung, wenn Sie Git-Merges nur in kleinen ersten Schritten in einem älteren SVN-Projekt verwenden möchten.

Wenn Sie mehr vertraut mit git Verschmelzung werden, können Sie andere Workflows erkunden: Wenn Sie wissen , was Sie tun, Sie können git verschmilzt mit svn verschmilzt mischen ( git-svn (oder ähnlich) nur mit svn merge zu helfen? )

Yaakov Belch
quelle
Dies scheint unnötig komplex. Ich habe von Leuten gehört, die das tun git merge --squash work, warum nicht? Ich kann sehen, wie Squash-Commits in der Verzweigung vor dem Zusammenführen ausgeführt werden, wenn Sie mehr als eine Auswahl erstellen (sagen wir, Sie haben 8 Commits und verwandeln jedes von 4 Commits in 1 und führen 2 Commits zum Master zusammen). Auch wenn ich meinen ' rebase
Arbeits'
8

Greg Hewgill Antwort oben ist nicht sicher! Wenn zwischen den beiden "git svn rebase" neue Commits im Trunk angezeigt werden, erfolgt die Zusammenführung nicht schnell.

Dies kann sichergestellt werden, indem das Flag "--ff-only" für die Git-Zusammenführung verwendet wird. Normalerweise führe ich jedoch nicht "git svn rebase" in der Verzweigung aus, sondern nur "git rebase master" (vorausgesetzt, es handelt sich nur um eine lokale Datei) Ast). Danach ist ein "git merge thebranch" garantiert schnell vorwärts.

Marius K.
quelle
6

Ein sicherer Weg, um svn-Zweige in git zusammenzuführen, ist die Verwendung von git merge --squash. Dadurch wird ein einzelnes Commit erstellt und das Hinzufügen einer Nachricht gestoppt.

Angenommen, Sie haben einen Themen-SVN-Zweig namens SVN-Zweig.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

Zu diesem Zeitpunkt haben Sie alle Änderungen aus dem SVN-Zweig in einem Commit zusammengefasst, das im Index wartet

git commit
luntain
quelle
Wie andere bereits betont haben, verliert dies jedoch die Granularität der Commits.
Kzqai
@Tchalvak: Manchmal willst du genau das. Ich habe oft Commits in einem Feature-Zweig ala "Fixed Mess von vorherigen Commit" und diese Sackgassen verstecke ich gerne wegen eines guten Rufs :-)
schoetbi
1
Ja, obwohl ich das für eine Gratwanderung halte, verlieren Sie bei jedem Squash auch die Fähigkeit, einzelne Commits später auseinander zu ziehen. Nur eine Frage der unterschiedlichen Auswahl von Standards.
Kzqai
1
@schoetbi Ja, manchmal müssen Sie den unordentlichen Verlauf vor dem Veröffentlichen bereinigen. Hier ist 'git rebase -i <trunk>' eine große Hilfe: Sie können Commits beitreten / neu anordnen / entfernen und sogar (!) Aufteilen, msg selektiv reparieren.
Inger
5

Setzen Sie den lokalen Git-Zweig auf den Master-Git-Zweig und dann auf dcommit um. Auf diese Weise haben Sie anscheinend alle diese Commits nacheinander ausgeführt, sodass SVN-Benutzer ihn linear sehen können, wie sie es gewohnt sind. Angenommen, Sie haben einen lokalen Zweig namens Thema, den Sie tun könnten

git rebase master topic

Das wird dann Ihre Commits über den Hauptzweig abspielen, damit Sie sie festschreiben können

JoeyJ
quelle