Wie können Sie nur einige Ihrer lokalen Git-Commits pushen?

160

Angenommen, ich habe 5 lokale Commits. Ich möchte nur zwei davon in ein zentrales Repo verschieben (mithilfe eines SVN-ähnlichen Workflows). Wie mache ich das?

Das hat nicht funktioniert:

git checkout HEAD~3  #set head to three commits ago
git push #attempt push from that head

Das führt dazu, dass alle 5 lokalen Commits verschoben werden.

Ich nehme an, ich könnte git zurücksetzen, um meine Commits tatsächlich rückgängig zu machen, gefolgt von git stash und dann git push - aber ich habe bereits Commit-Nachrichten geschrieben und Dateien organisiert und möchte sie nicht wiederholen.

Mein Gefühl ist, dass eine Flagge, die zum Drücken oder Zurücksetzen übergeben wurde, funktionieren würde.

Wenn es hilft, hier ist meine Git-Konfiguration

[ramanujan:~/myrepo/.git]$cat config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ssh://server/git/myrepo.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
Ramanujan
quelle

Antworten:

192

Angenommen, Ihre Commits befinden sich im Hauptzweig und Sie möchten sie in den Remote-Hauptzweig übertragen:

$ git push origin master~3:master

Wenn Sie git-svn verwendet haben:

$ git svn dcommit master~3

Im Fall von git-svn können Sie auch HEAD ~ 3 verwenden, da ein Commit erwartet wird. Im Fall von Straight Git müssen Sie den Zweignamen verwenden, da HEAD in der Referenz nicht richtig ausgewertet wird.

Sie könnten auch einen längeren Ansatz wählen:

$ git checkout -b tocommit HEAD~3
$ git push origin tocommit:master

Wenn Sie sich diese Art von Arbeitsabläufen zur Gewohnheit machen, sollten Sie in Betracht ziehen, Ihre Arbeit in einem separaten Zweig auszuführen. Dann könnten Sie so etwas tun wie:

$ git checkout master
$ git merge working~3
$ git push origin master:master

Beachten Sie, dass der Teil "Ursprungsmaster: Master" für Ihr Setup wahrscheinlich optional ist.

Ryan Graham
quelle
14
Hinweis: Sie müssen nicht verwenden master~3. Jeder Verweis auf das gewünschte Commit "bis zu" ist gleichermaßen gültig, z. B. HEAD~3oder HEAD~~~oder die spezifische SHA oder ein Tag, das das Commit kennzeichnet.
Kaz
2
Gutes Zeug. Eine Warnung: Diese Beispiele werden an den Ursprungsmaster gesendet. Wenn Sie diese Lösung kopieren und einfügen, wird der Master-Zweig möglicherweise versehentlich aktualisiert. (Natürlich sollten Sie immer vorsichtig sein und Ihren Befehl überprüfen, bevor Sie eine git push...)
Nofinator
Es scheint, dass dies das Commit drückt, aber den Zweig nicht remote hinzufügt.
Nateowami
@Nateowami dafür müssen Sie etwas anderes als masterfür die entfernte Seite der refspec angeben, wiegit push origin tocommit:newbramch
Ryan Graham
Aha. Der Filialname war bereits lokal vorhanden. Das hat mir wohl nicht gefallen. Die Fernbedienung hatte jedoch noch keinen Filialnamen.
Nateowami
16

Ich arbeite an einer lokalen Niederlassung namens "Arbeit". Dieser Zweig enthält alle temporären Commits (wie Problemumgehungen oder private Build-Optionen oder was auch immer), die ich nicht in das Upstream-Repository übertragen möchte. Ich arbeite an diesem Zweig. Wenn ich dann einen Commit durchführen möchte, wechsle ich zum Master-Zweig, wähle die entsprechenden Commits aus, die ich festschreiben möchte, und drücke dann den Master.

Nachdem ich Änderungen vom Upstream in meinen Hauptzweig gezogen habe, haben ich git checkout workund git rebase master. Das schreibt alle meine lokalen Änderungen neu, um am Ende der Geschichte zu sein.

Ich verwende git svndiesen Workflow tatsächlich , daher umfasst meine "Push" -Operation git svn dcommit. Ich benutze auch tigeinen netten GUI-Repository-Viewer im Textmodus, um die entsprechenden Commits für den Master auszuwählen.

Greg Hewgill
quelle
Mit git svn dcommit können Sie ein Commit für dcommit bis zu angeben, sodass der gewünschte Effekt mit git-svn ziemlich trivial ist.
Ryan Graham
Dieser Ansatz weist Nachteile auf (hier zusammengefasst auf stackoverflow.com/a/881014/1116674 ). Eine gute Alternative besteht darin, Zweige für jedes Feature, an dem Sie arbeiten, und einen workZweig zu erstellen . Anschließend führen Sie bestimmte Zweige zusammen, masterdamit Sie nicht den Verlauf verlieren. Wenn Sie mit arbeiten work, führen Sie alle Ihre Zweige darin zusammen. Es ist mehr Aufwand, aber es könnte sich in einigen Fällen lohnen.
Hudon
16

Standardmäßig werden mit git-push alle Zweige verschoben. Wenn Sie dies tun:

 git checkout HEAD~3  #set head to three commits ago
 git push #attempt push from that head

Sie wechseln zu einem getrennten HEAD (Sie befinden sich nicht in einem Zweig) und übertragen dann alle Zweige, einschließlich des lokalen Masters (der sich noch dort befindet), zum Remote-Master.

Die manuelle Lösung lautet:

 git push origin HEAD:master

Wenn Sie das Standardverhalten beim Verschieben aller Zweige verwirrend (und gefährlich!) Finden, fügen Sie dies zu Ihrer ~ / .gitconfig hinzu:

 [remote.origin]
    push = HEAD

Dann wird nur der Zweig gedrückt, auf dem Sie sich befinden. In Ihrem Beispiel (ein abgenommener Kopf) hätten Sie diese Fehlermeldung erhalten, anstatt versehentlich die falschen Commits zu drücken:

 error: unable to push to unqualified destination: HEAD
Thomas Leonard
quelle
10

Kurze Antwort:

git push <latest commit SHA1 until you want commits to be pushed>

Beispiele:

git push fc47b2

git push HEAD~2

Lange Antwort:

Commits sind als Kette mit einem Eltern / Kind-Mechanismus verbunden. Durch das Drücken eines Commits werden also auch alle übergeordneten Commits zu diesem Commit verschoben, die der Remote nicht bekannt waren. Dies geschieht implizit, wenn Sie git pushdas aktuelle Commit ausführen: Alle vorherigen Commits werden ebenfalls gepusht, da dieser Befehl äquivalent zu ist git push HEAD.

So könnte die Frage in Wie man ein bestimmtes Commit pusht, umgeschrieben werden , und dieses spezifische Commit könnte beispielsweise HEAD ~ 2 sein.

Wenn die Commits, die Sie pushen möchten, nicht aufeinander folgen, ordnen Sie sie einfach mit einem git rebase -ivor dem jeweiligen Push neu an .

Tim
quelle
5

1) Verwenden Sie "git rebase", um Ihre Commits neu zu ordnen, wenn Sie möchten.

git rebase -i

Dieser Befehl zeigt so etwas in Ihrem Editor an (ich verwende vim).

pick 4791291 commitA
pick a2bdfbd commitB
pick c3d4961 commitC
pick aa1cefc commitD
pick 9781434 commitE

# Rebase ..............
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out




^G Get Help         ^O WriteOut         ^R Read File        ^Y Prev Page                ^K Cut Text         ^C Cur Pos
^X Exit             ^J Justify          ^W Where Is         ^V Next Page            ^U UnCut Text       ^T To Spell

2) Ordnen Sie Ihre Commits nach Ihrer Wahl durch einfaches Ausschneiden und Einfügen neu an. Angenommen, die neue Bestellung lautet

Wählen Sie 9781434 commitE

Wählen Sie c3d4961 commitC

Wählen Sie 4791291 commitA

Wählen Sie aa1cefc commitD

Wählen Sie a2bdfbd commitB

Nehmen Sie diese Änderungen in Ihrem Editor vor und drücken Sie Strg + O (writeOut).

Oder Sie können auch verwenden

git rebase -i HEAD~<commitNumber>

Sie können die neue Sequenz mit überprüfen

git log

3) Jetzt verwenden

git push <remoteName> <commit SHA>:<remoteBranchName>

Wenn nur ein Zweig entfernt (Ursprung) und einer lokal (Master) ist, verwenden Sie einfach

git push <commit SHA>
git push aa1cefc

Dies wird commitB und commitD drücken.

Yogesh Yadav
quelle