Posso dividir um pedaço já dividido com o git?

205

Eu descobri recentemente a patchopção do git para o addcomando, e devo dizer que é realmente um recurso fantástico. Também descobri que um pedaço grande pode ser dividido em pedaços menores pressionando a stecla, o que aumenta a precisão do commit. Mas e se eu quiser ainda mais precisão, se o pedaço dividido não for pequeno o suficiente?

Por exemplo, considere este pedaço já dividido:

@@ -34,12 +34,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

Como posso adicionar a remoção de comentários CSS apenas para a próxima confirmação? A sopção não está mais disponível!

greg0ire
fonte

Respostas:

254

Se você estiver usando git add -pe mesmo depois de se separar s, não terá uma alteração pequena o suficiente, poderá eeditar o patch diretamente.

Isso pode ser um pouco confuso, mas se você seguir cuidadosamente as instruções na janela do editor que serão abertas após pressionar e, você estará bem. No caso que você citou, convém substituí-lo -por um espaço no início dessas linhas:

-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {

... e exclua a seguinte linha, ou seja, a que começa com +. Se você salvar e sair do editor, apenas a remoção do comentário CSS será realizada.

Mark Longair
fonte
9
Solução legal! Eu vi isso, mas não entendi ... eu acho que as mudanças também seriam removidas da árvore de trabalho.
9118 greg0ire
7
Na verdade, isso não é muito óbvio no texto de ajuda. Eu me encontrar com muito isso, na verdade, desde que eu acho que git realmente incentiva você a fazer cada commit tão preciso e belo quanto possível :)
Mark Longair
27
Observe que você realmente precisa substituí-lo por um espaço . Tentei imaginando que era possível excluir os -caracteres e o Git reclamou que meu patch não se aplicava.
21811 Ryan Lundy
3
Suponho que o motivo pelo qual você exclui as linhas com '-' e substitua '+' s por um espaço é que você está formando um patch em que essas linhas com '-' já foram removidas e as linhas com ' + já foram adicionados (nos olhos do patch). Ou outra maneira de ver, você realmente executa a ação que esses caracteres (-, +) representam (adicionando ou removendo uma linha). Somente as linhas restantes com '-'s e' + 's são registradas como alterações e o restante é "exatamente como o arquivo está".
Atomictom
3
@Filype: Eu não sei por que isso teria acontecido, eu tenho medo - se você estivesse executando git add -pe editando um pedaço com eisso, afetaria apenas o que está sendo preparado, não a sua árvore de trabalho.
Mark Longair
60

Digamos que sua example.cssaparência seja assim:

.classname {
  width: 440px;
}

/*#field_teacher_id {
  display: block;
} */

form.table-form #field_teacher + label,
form.table-form #field_producer_distributor + label {
  width: 300px;
}

.another {
  width: 420px;
}

Agora vamos mudar os seletores de estilo no bloco do meio e, enquanto estamos nisso, exclua um estilo antigo comentado que não precisamos mais.

.classname {
  width: 440px;
}

#user-register form.table-form .field-type-checkbox label {
  width: 300px;
}

.another {
  width: 420px;
}

Isso foi fácil, agora vamos cometer. Mas espere, eu quero manter a separação lógica das alterações no controle de versão para uma simples revisão passo a passo do código e para que minha equipe e eu possamos pesquisar facilmente o histórico de confirmação para obter detalhes específicos.

A exclusão do código antigo é logicamente separada da outra alteração do seletor de estilo. Vamos precisar de dois commits distintos, então vamos adicionar pedaços para um patch.

git add --patch
diff --git a/example.css b/example.css
index 426449d..50ecff9 100644
--- a/example.css
+++ b/example.css
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

Stage this hunk [y,n,q,a,d,/,e,?]?

Opa, parece que as mudanças estão muito próximas, então o git as juntou.

Mesmo tentando dividi- lo pressionando stem o mesmo resultado, porque a divisão não é granular o suficiente para nossas alterações de precisão. Linhas inalteradas são necessárias entre as linhas alteradas para que o git possa dividir automaticamente o patch.

Então, vamos editá- lo manualmente pressionandoe

Stage this hunk [y,n,q,a,d,/,e,?]? e

O git abrirá o patch em nosso editor de escolha.

# Manual hunk edit mode -- see bottom for a quick guide
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */
-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

Vamos revisar o objetivo:

Como posso adicionar a remoção de comentários CSS apenas para a próxima confirmação?

Queremos dividir isso em dois commits:

  1. A primeira confirmação envolve a exclusão de algumas linhas (remoção de comentários).

    Para remover as linhas comentadas, deixe-as em paz, elas já estão marcadas para rastrear as exclusões no controle de versão como queremos.

    -/*#field_teacher_id {
    - display: block;
    -} */

  2. O segundo commit é uma alteração, que é rastreada pelo registro de exclusões e adições:

    • Exclusões (linhas de seleção antigas removidas)

      Para manter as linhas antigas do seletor (não as exclua durante este commit), queremos ...

      Para remover as linhas '-', faça-as ''

      ... o que significa literalmente substituir os -sinais de menos por um caractere de espaço .

      Então, essas três linhas ...

      -
      -form.table-form #field_teacher + label,
      -form.table-form #field_producer_distributor + label {

      ... se tornará ( observe o espaço único na primeira de todas as 3 linhas):


      form.table-form #field_teacher + label,
      form.table-form #field_producer_distributor + label {

    • Adições (nova linha de seleção adicionada)

      Para não prestar atenção à nova linha de seletor adicionada durante esse commit, queremos ...

      Para remover as linhas '+', exclua-as.

      ... o que significa literalmente excluir toda a linha:

      +#user-register form.table-form .field-type-checkbox label {

      (Bônus: se você estiver usando o vim como seu editor, pressione ddpara excluir uma linha. Usuários do Nano pressione Ctrl+ K)

Seu editor deve ficar assim quando você salvar:

# Manual hunk edit mode -- see bottom for a quick guide
@@ -2,12 +2,7 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */

 form.table-form #field_teacher + label,
 form.table-form #field_producer_distributor + label {
   width: 300px;
 }

# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.

Agora vamos cometer.

git commit -m "remove old code"

E só para ter certeza, vamos ver as alterações desde o último commit.

git show
commit 572ecbc7beecca495c8965ce54fbccabdd085112
Author: Jeff Puckett <[email protected]>
Date:   Sat Jun 11 17:06:48 2016 -0500

    remove old code

diff --git a/example.css b/example.css
index 426449d..d04c832 100644
--- a/example.css
+++ b/example.css
@@ -2,9 +2,6 @@
   width: 440px;
 }

-/*#field_teacher_id {
-  display: block;
-} */

 form.table-form #field_teacher + label,
 form.table-form #field_producer_distributor + label {

Perfeito - você pode ver que apenas as exclusões foram incluídas nesse commit atômico. Agora vamos terminar o trabalho e comprometer o resto.

git add .
git commit -m "change selectors"
git show
commit 83ec3c16b73bca799e4ed525148cf303e0bd39f9
Author: Jeff Puckett <[email protected]>
Date:   Sat Jun 11 17:09:12 2016 -0500

    change selectors

diff --git a/example.css b/example.css
index d04c832..50ecff9 100644
--- a/example.css
+++ b/example.css
@@ -2,9 +2,7 @@
   width: 440px;
 }

-
-form.table-form #field_teacher + label,
-form.table-form #field_producer_distributor + label {
+#user-register form.table-form .field-type-checkbox label {
   width: 300px;
 }

Finalmente, você pode ver que o último commit inclui apenas as alterações do seletor.

Jeff Puckett
fonte
1
Bônus # 2: Se acontecer de você estar usando vim como o seu editor, você tem que pressionar "d" duas vezes no teclado para apagar uma linha: D
Alexxus
3
Além disso, em vez de remover as linhas adicionadas que você não deseja adicionar, você pode substituir +por #. O resultado é o mesmo, mas talvez você não se sinta à vontade ao excluir (e não possa reverter) ou queira experimentar antes de salvar.
ob-ivan
E isso, para o vim r #acabou o plus xD
aksh1618
O objetivo é "Como adicionar a remoção de comentários CSS apenas ao próximo commit?", Mas as etapas são realmente confusas quanto ao que está sendo realizado. (queremos "adicionar" apenas a "remoção" das poucas linhas para a próxima confirmação.) Então, basta dizer remover ou adicionar é muito confuso. Declarar o que foi realizado em cada etapa ajudaria a esclarecer.
ahnbizcad
9

Se você pode usar o git gui, ele permite que você defina as alterações linha por linha. Infelizmente, não sei como fazer isso na linha de comando - ou mesmo se for possível.

Uma outra opção que usei no passado é reverter parte da mudança (manter o editor aberto), confirmar os bits que quero, desfazer e salvar novamente a partir do editor. Não é muito elegante, mas faz o trabalho. :)


EDIT (uso do git-gui):

Não tenho certeza se o git-gui é o mesmo nas versões msysgit e linux, usei apenas o msysgit. Mas, supondo que seja o mesmo, quando você o executa, existem quatro painéis: o painel superior esquerdo é o seu diretório de trabalho alterado, o inferior esquerdo é o seu estágio, o superior direito é o diff do arquivo selecionado (seja dir de trabalho ou encenado), e o canto inferior direito é para a descrição do commit (suspeito que você não precise dele). Quando você clica em um arquivo no canto superior direito, verá o diff. Se você clicar com o botão direito do mouse em uma linha de diferenças, verá um menu de contexto. As duas opções a serem observadas são "estágio hunk para confirmação" e "linha de estágio para confirmação". Você continua selecionando "linha de estágio para confirmação" nas linhas que deseja confirmar e está pronto. Você pode até selecionar várias linhas e prepará-las, se desejar.

Quanto à confirmação, você pode usar a ferramenta GUI ou a linha de comando.

vhallac
fonte
Sua segunda proposição é bastante evidente, mas a primeira é interessante, você poderia detalhar um pouco mais? Eu instalei, git-guimas não tenho idéia de como conseguir o que você está descrevendo.
8119 greg0ire
tanques muito! Isso funciona! Consegui até selecionar as linhas que queria exibir e indexá-las com um clique.
8118 greg0ire
0

Uma maneira de fazer isso é pular o pedaço, o git addque mais você precisar, e depois executargit add novamente. Se esse for o único pedaço, você poderá dividi-lo.

Se você está preocupado com a ordem dos commits, use git rebase -i.

Abizern
fonte
Foi isso que tentei, e o pedaço da minha pergunta é o único quando corro git add -pnovamente, mas não posso dividi-lo. Eu entendi: Stage this hunk [y,n,q,a,d,/,e,?]?e depois pressionar 's' imprime a ajuda. BTW, você quis dizer add patch, não patch add? Ou existe um git patchplugin que devo instalar?
greg0ire
Você cometeu os pedaços encenados antes de executar isso novamente? E não, o Mercurial tem plugins, o Git não.
Abizern
Não, não, eu quero que eles estejam no mesmo commit (mas acho que se sua solução funcionar, eu posso usar --amend para conseguir isso). Vou dar uma chance.
8119 greg0ire
Como minha resposta dizia → git rebase -i. Que é mais flexível do quecommit --amend
Abizern