come spingi solo alcuni dei tuoi commit git locali?


160

Supponiamo che io abbia 5 commit locali. Voglio inviarne solo 2 a un repository centralizzato (usando un flusso di lavoro in stile SVN). Come faccio a fare questo?

Questo non ha funzionato:

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

Questo finisce per spingere tutti e 5 gli commit locali.

Suppongo che potrei fare git reset per annullare effettivamente i miei commit, seguito da git stash e poi git push - ma ho già scritto messaggi di commit e file organizzati e non voglio rifarli.

La mia sensazione è che alcuni flag passati a push o reset funzionino.

Se aiuta, ecco la mia configurazione di git

[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

Risposte:


192

Supponendo che i tuoi commit siano sul ramo master e vuoi spingerli sul ramo master remoto:

$ git push origin master~3:master

Se stavi usando git-svn:

$ git svn dcommit master~3

Nel caso di git-svn, potresti anche usare HEAD ~ 3, poiché si aspetta un commit. Nel caso di straight git, è necessario utilizzare il nome del ramo perché HEAD non viene valutato correttamente in refspec.

Potresti anche adottare un approccio più lungo di:

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

Se stai prendendo l'abitudine di questo tipo di flusso di lavoro, dovresti considerare di fare il tuo lavoro in un ramo separato. Quindi potresti fare qualcosa del tipo:

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

Si noti che la parte "origin master: master" è probabilmente facoltativa per la configurazione.


14
Nota: non è necessario utilizzare master~3. Qualsiasi riferimento al commit "fino a" desiderato è ugualmente valido, come HEAD~3o HEAD~~~, o lo SHA specifico, o un tag che identifica quel commit.
Kaz

2
Roba buona. Un avvertimento però: questi esempi vanno al master di origine. Se stai copiando e incollando questa soluzione, potresti finire per aggiornare accidentalmente il ramo principale. (Certo, dovresti sempre stare attento e ricontrollare il tuo comando prima di emettere un git push...)
nofinator

Sembra che questo spinga il commit, ma non aggiunge il ramo da remoto.
Nateowami,

@Nateowami per questo dovrai specificare qualcosa di diverso rispetto masteral lato remoto del refspec, comegit push origin tocommit:newbramch
Ryan Graham,

Vedo. Il nome del ramo esisteva già localmente; Suppongo che non gli piacesse. Il telecomando non aveva ancora il nome del ramo.
Nateowami,

16

Quello che faccio è lavorare su un ramo locale chiamato "lavoro". Questo ramo contiene tutti i commit temporanei (come soluzioni alternative o opzioni di build private o altro) che non intendo inviare al repository upstream. Io lavoro via su quel ramo, poi quando ho voglia di impegnarsi posso passare al ramo principale, cherry-pick i commit appropriate che non voglio impegnare, quindi spingere maestro.

Dopo aver inserito le modifiche dall'upstream nel mio ramo principale, I git checkout worke git rebase master. Ciò riscrive tutti i miei cambiamenti locali per essere alla fine della storia.

In realtà sto usando git svnquesto flusso di lavoro, quindi la mia operazione "push" comporta git svn dcommit. Uso anche il tigvisualizzatore di repository gui in modalità testo, per selezionare i commit appropriati da padroneggiare.


con git svn dcommit, è possibile specificare un commit per dcommit fino a, quindi l'effetto desiderato è abbastanza banale con git-svn.
Ryan Graham,

Ci sono svantaggi di questo approccio (riassunto qui stackoverflow.com/a/881014/1116674 ). Una buona alternativa è quella di creare rami per ogni funzione su cui stai lavorando e un workramo. Quindi, unisci i rami specifici in mastermodo da non perdere la cronologia su di essi. Quando lavori con work, unisci tutti i tuoi rami. È più sovraccarico, ma in alcuni casi potrebbe valerne la pena.
Hudon,

16

Di default, git-push spinge tutti i rami. Quando lo fai:

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

Ti sposti in una HEAD distaccata (non sei su alcun ramo) e quindi spingi tutti i rami, incluso il master locale (che è ancora dove si trovava) sul master remoto.

La soluzione manuale è:

 git push origin HEAD:master

Se ritieni che il comportamento predefinito di spingere tutti i rami sia confuso (e pericoloso!), Aggiungi questo al tuo ~ / .gitconfig:

 [remote.origin]
    push = HEAD

Quindi viene spinto solo il ramo in cui ti trovi. Nel tuo esempio (una testa staccata), avresti ricevuto questo messaggio di errore, piuttosto che spingere accidentalmente i commit sbagliati:

 error: unable to push to unqualified destination: HEAD

10

Risposta breve:

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

Esempi:

git push fc47b2

git push HEAD~2

Risposta lunga:

I commit sono collegati insieme come una catena con un meccanismo genitore / figlio. Quindi, spingendo un commit in realtà si spinge anche tutti i commit genitore a questo commit che non erano noti al telecomando. Questo è implicitamente fatto quando si esegue git pushil commit corrente: vengono spinti anche tutti i commit precedenti perché questo comando è equivalente a git push HEAD.

Quindi la domanda potrebbe essere riscritta in Come inviare un commit specifico e questo commit specifico potrebbe essere HEAD ~ 2, per esempio.

Se i commit che vuoi spingere sono non consecutivi, semplicemente riordinali con un pushgit rebase -i prima dello specifico push .


5

1) Usa "git rebase" per riordinare i tuoi commit, se vuoi.

git rebase -i

Questo comando mostrerà qualcosa del genere nel tuo editor (sto usando 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) Riordina i tuoi impegni secondo la tua scelta con una semplice pasta tagliata. Supponiamo che il nuovo ordine sia

scegli 9781434 commitE

scegli c3d4961 commitC

scegli 4791291 commitA

scegli aa1cefc commitD

scegli a2bdfbd commitB

Apporta queste modifiche nel tuo editor e premi ctrl + O (writeOut)

Oppure puoi anche usare

git rebase -i HEAD~<commitNumber>

Puoi controllare la nuova sequenza con

git log

3) Ora usa

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

Se solo un ramo in remoto (origine) e uno in locale (master), basta usare

git push <commit SHA>
git push aa1cefc

Questo spingerà commitB e commitD.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.