como você envia apenas alguns de seus compromissos locais do git?

160

Suponha que eu tenha 5 confirmações locais. Quero enviar apenas dois deles para um repositório centralizado (usando um fluxo de trabalho no estilo SVN). Como eu faço isso?

Isso não funcionou:

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

Isso acaba forçando todos os 5 commits locais.

Suponho que eu poderia fazer o git reset para desfazer meus commits, seguidos por git stash e git push - mas eu já tenho mensagens de commit escritas e arquivos organizados e não quero refazê-los.

Meu sentimento é que alguma bandeira passada para empurrar ou redefinir funcionaria.

Se ajudar, aqui está minha configuração do 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
ramanujan
fonte

Respostas:

192

Supondo que suas confirmações estejam na ramificação principal e você deseja enviá-las para a ramificação principal remota:

$ git push origin master~3:master

Se você estava usando o git-svn:

$ git svn dcommit master~3

No caso do git-svn, você também pode usar o HEAD ~ 3, pois espera um commit. No caso do straight git, você precisa usar o nome do ramo, porque o HEAD não é avaliado corretamente no refspec.

Você também pode adotar uma abordagem mais longa de:

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

Se você está habituando esse tipo de fluxo de trabalho, considere fazer seu trabalho em um ramo separado. Então você pode fazer algo como:

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

Observe que a parte "origin master: master" é provavelmente opcional para sua configuração.

Ryan Graham
fonte
14
Nota: você não precisa usar master~3. Qualquer referência ao commit "até" desejado é igualmente válida, como HEAD~3or HEAD~~~, ou o SHA específico, ou uma tag que rotula esse commit.
Kaz
2
Coisa boa. Um aviso: esses exemplos são enviados para o mestre de origem. Se você estiver copiando e colando esta solução, pode acabar atualizando acidentalmente a ramificação principal. (Claro, você deve sempre ter cuidado e verifique o seu comando antes de emitir uma git push...)
nofinator
Parece que isso empurra a confirmação, mas não adiciona a ramificação remotamente.
Nateowami 17/02
@Nateowami para isso você precisará especificar algo diferente masterpara o lado remoto do refspec, comogit push origin tocommit:newbramch
Ryan Graham
Entendo. O nome da filial já existia localmente; Suponho que não goste disso. O controle remoto ainda não tinha o nome da filial.
Nateowami 17/02
16

O que faço é trabalhar em uma filial local chamada "trabalho". Este ramo contém todas as confirmações temporárias (como soluções alternativas ou opções de compilação privadas ou qualquer outra coisa) que eu não pretendo enviar ao repositório upstream. Eu trabalho nessa ramificação e, quando quero confirmar, mudo para a ramificação mestre e escolho as confirmações apropriadas que faço querem se comprometer, em seguida, empurre mestre.

Depois de puxar as alterações do montante para o meu ramo principal, eu git checkout worke git rebase master. Isso reescreve todas as minhas alterações locais para estarem no final da história.

Na verdade, estou usando git svnesse fluxo de trabalho, portanto, minha operação "push" envolve git svn dcommit. Eu também uso o tigque é um visualizador de repositório de GUI de modo de texto agradável, para escolher as confirmações apropriadas para dominar.

Greg Hewgill
fonte
com o git svn dcommit, você pode especificar um commit para o dcommit até, portanto o efeito desejado é bastante trivial com o git-svn.
22611 Ryan Graham
Existem desvantagens para essa abordagem (resumida aqui stackoverflow.com/a/881014/1116674 ). Uma boa alternativa é criar ramificações para cada recurso em que você está trabalhando e uma workramificação. Em seguida, você mescla ramificações específicas para masternão perder o histórico delas. Ao trabalhar com work, você mescla todos os seus ramos nele. É mais caro, mas pode valer a pena em alguns casos.
Hudon
16

Por padrão, o git-push empurra todas as ramificações. Quando você faz isso:

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

Você move para um HEAD desanexado (você não está em nenhum ramo) e envia todos os ramos, incluindo o mestre local (que ainda está onde estava) para o mestre remoto.

A solução manual é:

 git push origin HEAD:master

Se você achar que o comportamento padrão de empurrar todos os ramos é confuso (e perigoso!), Adicione isso ao seu ~ / .gitconfig:

 [remote.origin]
    push = HEAD

Somente o ramo em que você está é enviado. No seu exemplo (um cabeçalho desanexado), você receberia esta mensagem de erro, em vez de enviar acidentalmente os commits errados:

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

Resposta curta:

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

Exemplos:

git push fc47b2

git push HEAD~2

Resposta longa:

As confirmações são vinculadas como uma cadeia com um mecanismo pai / filho. Assim, enviar um commit na verdade também empurra todos os commit pai para esse commit que não são conhecidos pelo controle remoto. Isso é feito implicitamente quando você git pushefetua o commit atual: todos os commits anteriores também são enviados porque esse comando é equivalente a git push HEAD.

Portanto, a pergunta pode ser reescrita em Como enviar por push um commit específico e esse commit específico pode ser HEAD ~ 2, por exemplo.

Se as confirmações que você deseja enviar não forem consecutivas, basta reordená-las com um git rebase -iantes da solicitação específica .

Tim
fonte
5

1) Use "git rebase" para reordenar seus commits, se desejar.

git rebase -i

Este comando exibirá algo assim no seu editor (estou usando o 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) Reordene seus seus commits de acordo com sua escolha com uma pasta de corte simples. Suponha que o novo pedido seja

escolher 9781434 commitE

escolher c3d4961 commitC

escolha 4791291 commitA

escolha aa1cefc commitD

escolha a2bdfbd commitB

Faça essas alterações no seu editor e pressione ctrl + O (writeOut)

Ou você também pode usar

git rebase -i HEAD~<commitNumber>

Você pode verificar a nova sequência com

git log

3) Agora use

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

Se apenas uma ramificação no controle remoto (origem) e uma no local (mestre), basta usar

git push <commit SHA>
git push aa1cefc

Isso empurrará o commitB e o commitD.

Yogesh Yadav
fonte