Por que o squash git se compromete com solicitações pull?


200

Por que todo repositório sério do Github que recebo solicita que eu comprima meus commits em um único commit?

Eu pensei que o log do git estava lá para que você pudesse inspecionar todo o seu histórico e ver exatamente quais mudanças aconteceram onde, mas esmagá-lo o puxa para fora do histórico e agrupa tudo em um commit. Qual é o ponto?

Isso também parece ir contra o mantra "comprometer-se cedo e comprometer-se com frequência".


11
Para um grande repositório, economiza muito tempo e espaço quando as pessoas querem fazer algo com o repositório.
raptortech97

8
" Isso também parece ir contra o mantra" comprometer-se cedo e comprometer-se com frequência ". - Não, você compromete-se cedo / frequentemente, mas não necessariamente deseja PR a cada pequena alteração. E o revisor não quer examiná-los, isso é certo.
JensG

7
Não é necessário colocar uma edição na pergunta para resumir qual é a resposta percebida. Esse é o lugar de respostas aceitas e votos positivos. As pessoas podem tirar suas próprias conclusões lendo as respostas.

5
Ainda não entendo por que existe um desejo de esmagar os commits. Eu acho que há muitos contras em esmagar com quase nenhum profissional. É uma questão de apresentação disfarçada como uma questão de dados.
Mkobit

3
Qualquer análise útil no log é perdida com abóboras. Parece que um desenvolvedor criou um recurso com uma única confirmação. Também é difícil ver por que existe um código estranho. O histórico de um recurso pode ser importante e pode ajudar bastante a explicar por que o código é como é. A visão concisa do log é útil, mas não pode ser alcançada de outra maneira?
Tjaart 24/04

Respostas:


158

Para que você tenha um histórico do Git claro e conciso, que documente de forma clara e fácil as alterações feitas e os motivos.

Por exemplo, um log git típico 'sem interrupção' para mim pode se parecer com o seguinte:

7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
787g8fgf78... hotfix for #5849564648
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Que bagunça!

Enquanto um log git gerenciado e mesclado com mais cuidado, com um pouco de foco adicional nas mensagens para isso, pode parecer:

7hgf8978g9... 8483948393 Added new slideshow feature
787g8fgf78... 5849564648 Hotfix for android display issue
9080gf6567... 6589685988 Implemented pop-up to select language

Eu acho que você pode ver o ponto de esmagar os commits em geral e o mesmo princípio se aplica aos pedidos de pull - Legibilidade da História. Você também pode adicionar um log de confirmação que já possui centenas ou mesmo milhares de confirmações e isso ajudará a manter o histórico de crescimento curto e conciso.

Você quer se comprometer cedo e com frequência. É uma prática recomendada por vários motivos. Acho que isso me leva a ter confirmações frequentes que são "wip" (trabalho em andamento) ou "parte A concluída" ou "erro tipográfico, correção menor" em que estou usando o git para me ajudar a trabalhar e fornecer pontos de trabalho que Posso voltar para se o código a seguir não estiver funcionando conforme progredir para que as coisas funcionem. No entanto, eu não preciso ou quero esse histórico como parte do histórico final do git, para que eu possa reprimir meus commits - mas veja as notas abaixo sobre o que isso significa em um ramo de desenvolvimento vs. mestre.

Se houver marcos importantes que representam estágios de trabalho distintos, ainda é aceitável ter mais de um commit por recurso / tarefa / bug. No entanto, isso geralmente destaca o fato de que o ticket em desenvolvimento é 'muito grande' e precisa ser dividido em partes menores que podem ser independentes, por exemplo:

8754390gf87... Implement feature switches

parece "1 trabalho". Ou eles existem ou não! Não parece fazer sentido divulgá-lo. No entanto, a experiência me mostrou que (dependendo do tamanho e da complexidade da organização) um caminho mais granular pode ser:

fgfd7897899... Add field to database, add indexes and a trigger for the dw group
9458947548g... Add 'backend' code in controller for when id is passed in url.
6256ac24426... Add 'backend' code to make field available for views.
402c476edf6... Add feature to UI

Peças pequenas significam revisões de código mais fáceis, testes de unidade mais fáceis, melhor oportunidade de qa, melhor alinhamento ao Princípio de Responsabilidade Única, etc.

Para os aspectos práticos de quando realmente fazer essas abóboras, existem basicamente dois estágios distintos que têm seu próprio fluxo de trabalho

  • seu desenvolvimento, por exemplo, solicitações pull
  • seu trabalho adicionado à ramificação da linha principal, por exemplo, mestre

Durante o seu desenvolvimento, você confirma 'cedo e frequentemente' e com mensagens rápidas 'descartáveis'. Você pode esmagar aqui algumas vezes, por exemplo, esmagar em confirmações de mensagens wip e todo. Tudo bem, dentro da ramificação, manter várias confirmações que representam etapas distintas que você fez no desenvolvimento. A maioria das abóboras que você escolhe fazer deve estar dentro desses ramos de recursos, enquanto eles estão sendo desenvolvidos e antes da mesclagem para dominar.
Ao adicionar à ramificação da linha principal, você deseja que as confirmações sejam concisas e formatadas corretamente de acordo com o histórico da linha principal existente. Isso pode incluir o ID do sistema do Ticket Tracker, por exemplo, o JIRA, como mostrado nos exemplos. O esmagamento não se aplica aqui, a menos que você queira 'acumular' vários commits distintos no master. Normalmente você não.

O uso --no-ffao mesclar para o mestre usará uma confirmação para a mesclagem e também preservará o histórico (na ramificação). Algumas organizações consideram isso uma prática recomendada. Veja mais em https://stackoverflow.com/q/9069061/631619 Você também verá o efeito prático em git logque um --no-ffcommit será o último commit, na parte superior do HEAD (quando terminar), enquanto que sem --no-ffele pode ser mais abaixo na história, dependendo das datas e outras confirmações.


30
Eu discordo completamente desta filosofia. Commits regulares pequenos dividem uma tarefa em tarefas menores e geralmente fornecem informações úteis sobre exatamente o que estava sendo trabalhado quando uma linha foi alterada. Se você possui pequenas confirmações "WIP", deve usar uma nova refazer interativa para limpar isso antes de enviá-las. Esmagar PRs parece derrotar completamente o ponto de pequenos e regulares IMHO. É quase desrespeitoso para o autor que se esforçou para fornecer mensagens de log com informações de confirmação específicas e você simplesmente joga tudo fora.
Jez

6
Interessante. No final do dia, baseio minha 'filosofia' no que vi funcionar. Para ser claro, dividir o trabalho em partes pequenas e comprometer cada uma delas é ótimo enquanto trabalhamos na mudança. Minha experiência foi que, uma vez que este trabalho foi mesclado para dominar, a principal coisa que tende a ser examinada é 'o que, em sua totalidade, o que é esse commit de mesclagem que (geralmente) causou problemas x ...
Michael Durrant

6
Eu também sou anti-squash. Você não deve escrever mensagens de confirmação tolas em primeiro lugar. E no exemplo (85493g2458 ... Corrigido o problema de exibição da apresentação de slides em ie) Isso seria muito mais útil se eu fosse depurar o código associado.
Thomas Davis

5
É lamentável que esse dogma descuidado e extraviado tenha surgido na prática de SCM. Ferramentas de filtragem devem ser utilizadas para gerenciar a legibilidade do histórico e não confirmar o comportamento.
Ctpenrose

4
tendências e dogmas perturbadores. uau Eu preferiria um argumento mais baseado em fatos, apresentado de uma maneira mais agradável. Você pode postar / votar em uma opinião diferente. Esse é o ponto de votação comunidade
Michael Durrant

26

Como muitas vezes a pessoa que puxa um PR se preocupa com o efeito líquido dos commits "recurso adicional X", não com os "modelos básicos, função X de correção de erros, função Y de adição, adição de função Y, erros de digitação corrigidos nos comentários, parâmetros ajustados de escala de dados, mapa de hash tem melhor desempenho do que" lista "... nível de detalhe

Se você acha que suas 16 confirmações são melhor representadas por 2 confirmações, em vez de 1 "Adicionado recurso X, Z re-fatorado para usar X", então provavelmente é bom propor um PR com 2 confirmações, mas talvez seja melhor propor 2 PRs separados nesse caso (se o representante ainda insistir em PRs de confirmação única)

Isso não vai contra o mantra "comprometer-se cedo e comprometer-se com frequência", como no seu repositório, enquanto você está desenvolvendo, ainda tem detalhes granulares, portanto, você tem poucas chances de perder o trabalho e outras pessoas podem revisar / puxar / propor pr contra seu trabalho enquanto o novo pr está sendo desenvolvido.


4
Mas quando você pratica squash, também perde essa história no garfo, certo? Por que você gostaria de jogar fora essa história?
Hamstar

4
a) se você deseja preservar o histórico em sua ramificação, é fácil ter uma ramificação "para mesclagem" e uma ramificação "dev" (que pode ser o que você precisa de qualquer maneira, pois pode estar adicionando novas confirmações ao seu desenvolvedor principal) ramo depois de propor o PR) b) você ainda está preservando o efeito líquido desses commits, seria apenas nos casos em que você deseja reverter ou escolher um subcomponente do PR que o indivíduo compromete seria necessário . Nesse caso, você provavelmente está fazendo várias coisas (por exemplo: correção de bug X, adicione o recurso Y) em um único PR e vários PR podem ser novamente necessários.
Andrew Hill

@AndrewHill que grande conceito! Quero apresentar esse fluxo de trabalho à minha equipe, como ele funcionou para você? Eu escrevi um post sobre o assunto e me deparei com esse comentário enquanto pesquisava.
Droogans

19

A principal razão do que eu posso ver é a seguinte:

  • A interface do usuário do GitHub para mesclar solicitações pull atualmente (outubro de 2015) não permite editar a primeira linha da mensagem de confirmação, forçando-a a ser Merge pull request #123 from joebloggs/fix-snafoo
  • A interface do usuário do GitHub para navegar no histórico de consolidação atualmente não permite visualizar o histórico da ramificação do --first-parentponto de vista
  • A interface do usuário do GitHub por observar a culpa em um arquivo atualmente não permite que você veja a culpa do arquivo com o --first-parentponto de vista (observe que isso foi corrigido apenas no Git 2.6.2, para que pudéssemos perdoar o GitHub por não ter isso. acessível)

Portanto, quando você combina todas as três situações acima, você obtém uma situação em que as confirmações não ordenadas que estão sendo mescladas parecem feias na interface do usuário do GitHub.

Seu histórico com confirmações esmagadas será algo como

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... Hotfix for android display issue
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... Implemented pop-up to select language

Considerando que, sem commits esmagados, a história parecerá algo como

1256556316... Merge pull request #423 from jrandom/add-slideshows
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... Merge pull request #324 from ahacker/fix-android-display
787g8fgf78... hotfix for #5849564648
f56556316e... Merge pull request #28 from somwhere/select-lang-popup
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Quando você tem muitos commits em um rastreamento de relações públicas em que ocorreu uma mudança, pode se tornar um pesadelo se você se restringir ao uso da interface do usuário do GitHub .

Por exemplo, você encontra um ponteiro nulo sendo des-referenciado em algum lugar de um arquivo ... então você diz "quem fez isso e quando? Que versões de lançamento são afetadas?". Em seguida, você passa pela visão de culpa na interface do usuário do GitHub e vê que a linha foi alterada em789fdfffdf... "mas espere um segundo, essa linha estava apenas tendo seu recuo alterado para se encaixar no restante do código", então agora você precisa navegar para o estado da árvore desse arquivo no commit pai e visitar novamente a página da culpa ... eventualmente você encontra o commit ... é um commit de 6 meses atrás ... "ah **** isso pode estar afetando os usuários por 6 meses" você diz ... ah mas espere, esse commit estava na verdade em uma solicitação pull e só foi mesclada ontem e ninguém fez um lançamento ainda ... "Porra, gente por fundir commits sem esmagar o histórico" é o clamor que geralmente pode ser ouvido após cerca de 2 ou 3 expedições de arqueologia de código via UI do GitHub

Agora, vamos considerar como isso funciona se você usar a linha de comando Git (e o incrível 2.6.2, que tem a correção git blame --first-parent)

  • Se você estivesse usando a linha de comando Git, seria capaz de controlar completamente a mensagem de consolidação de mesclagem e, portanto, a consolidação de mesclagem poderia ter uma boa linha de resumo.

Portanto, nosso histórico de submissões se pareceria

$ git log
1256556316... #423 Added new slideshow feature
7hgf8978g9... Added new slideshow feature, JIRA # 848394839
85493g2458... Fixed slideshow display issue in ie
gh354354gh... wip, done for the week
789fdfffdf... minor alignment issue
56556316ad... #324 Hotfix for android display issue
787g8fgf78... hotfix for #5849564648
f56556316e... #28 Implemented pop-up to select language
9080gf6567... implemented feature # 65896859
gh34839843... minor fix (typo) for 3rd test

Mas também podemos fazer

$ git log --first-parent
1256556316... #423 Added new slideshow feature
56556316ad... #324 Hotfix for android display issue
f56556316e... #28 Implemented pop-up to select language

(Em outras palavras: a CLI do Git permite que você coma e coma também)

Agora, quando atingimos o problema do ponteiro nulo ... bem, apenas usamos git blame --first-parent -w dodgy-file.ce recebemos imediatamente o commit exato em que a referência de ponteiro nulo foi introduzida no ramo mestre, ignorando alterações simples de espaço em branco.

Obviamente, se você estiver fazendo mesclagens usando a interface do usuário do GitHub, isso git log --first-parenté realmente péssimo, graças ao GitHub forçando a primeira linha da mensagem de confirmação de mesclagem:

1256556316... Merge pull request #423 from jrandom/add-slideshows
56556316ad... Merge pull request #324 from ahacker/fix-android-display
f56556316e... Merge pull request #28 from somwhere/select-lang-popup

Então, para resumir uma longa história:

A interface do usuário do GitHub (outubro de 2015) tem várias deficiências na maneira como mescla solicitações pull, como apresenta o histórico de confirmação e como atribui informações de culpa. A melhor maneira atual de solucionar esses defeitos na interface do usuário do GitHub é solicitar às pessoas que comprimam suas confirmações antes da mesclagem.

A CLI do Git não possui esses problemas e você pode escolher facilmente qual visualização deseja ver para descobrir o motivo pelo qual uma alteração específica foi feita dessa maneira (olhando o histórico dos commits sem interrupção), bem como veja os commits efetivamente esmagados.

Post Script

A razão final frequentemente citada para comprimir commits é facilitar o backporting ... se você tiver apenas um commit para back port (ou seja, o commit squashed), será fácil escolher facilmente ...

Bem, se você está olhando para a história do git git log --first-parent, pode escolher os commits de mesclagem. A maioria das pessoas se confunde com confirmações de mesclagem de seleção de cereja porque você precisa especificar a -m Nopção, mas se você recebeu a confirmação a partir de git log --first-parententão você sabe que é o primeiro pai que deseja seguir, assim serágit cherry-pick -m 1 ...


11
Boa resposta! Mas escolher uma gama com facilidade é bastante fácil, não tenho certeza se a compro como motivo.
Stiggler

11
@ stiggler Eu, pessoalmente , não o comprei como motivo, mas já o vi citado por outros como motivo. IMHO o ferramental git é o que você quer e esmagando commits é mau ... mas eu diria que ter conduzido a fixação de --first-parentmim mesmo, a fim de ganhar um argumento contra commits esmagando ;-)
Stephen Connolly

11
Essa deve ser a resposta aceita. Muito menos dogma e mostra que a filtragem do histórico pode ser usada para "comer o seu bolo e comê-lo também". Você não precisa aniquilar a história para trazer clareza às explorações de história do git.
Ctpenrose #

Não será tão fácil escolher se, pressionando seus commits, você acaba agrupando muitas alterações em arquivos diferentes em um único commit. Eu acho que isso varia muito de acordo com a base de código, quais alterações foram feitas, quantas pessoas estão colaborando e assim por diante. Não acho que possamos criar uma única regra geral válida em todos os casos.
Amy Pellegrini

2

Eu concordo com os sentimentos expressos em outras respostas neste tópico sobre mostrar um histórico claro e conciso dos recursos adicionados e dos bugs corrigidos. No entanto, gostaria de abordar outro aspecto que sua pergunta evitou, mas não declarou explicitamente. Parte do problema que você pode ter sobre alguns dos métodos de trabalho do git é que o git permite reescrever a história que parece estranha ao ser apresentada ao git após o uso de outras formas de controle de origem, onde tais ações são impossíveis. Além disso, isso também contraria o princípio geralmente aceito de controle de origem de que, quando algo for confirmado / verificado no controle de origem, você poderá reverter para esse estado, independentemente das alterações feitas após a confirmação. Como você sugere na sua pergunta, essa é uma dessas situações. Eu acho que o git é um ótimo sistema de controle de versão; no entanto, para entendê-lo, é necessário entender alguns dos detalhes de implementação e decisões de design subjacentes e, como resultado, possui uma curva de aprendizado mais acentuada. Lembre-se de que o git foi planejado para ser um sistema de controle de versão distribuído e isso ajudará a explicar por que os designers permitem que o histórico do git seja reescrito com o squash commits sendo um exemplo disso.


A rigor, o Git não permite que você mude o histórico. Os objetos de confirmação são imutáveis. São apenas ponteiros de ramificação que são mutáveis ​​e somente então com uma "atualização forçada", que geralmente é considerada algo que você faz apenas para fins de desenvolvimento, não na ramificação principal. Você sempre pode voltar a um estado anterior usando o reflog, a menos que git gctenha sido executado para descartar objetos não referenciados.
Jon Purdy

Você está correto, e talvez eu devesse ter mencionado isso acima. No entanto, eu argumentaria por algo como um envio forçado ou reenvio de confirmações que já foram enviadas para um repo remoto, se qualificaria como reescrever o histórico, pois a ordem e a organização dos envios são tão importantes quanto os próprios envios.
Fred Thomsen

Isso é verdade para um determinado commit. No quadro geral, penso em rebasing interativo e ramificação de filtro como reescrevendo efetivamente a história, alterando sua composição.
precisa

11
Para ser justo, o SVN mantém a abordagem "cada consolidação é sagrada", mas quando a mesclagem cria uma única consolidação com essa mesclagem e oculta as revisões que contribuíram para isso, portanto, parece que todas as mesclagens do SVN são "reestruturadas". Eles não são, você apenas precisa informar o comando history para mostrar os componentes mesclados. Eu gosto dessa abordagem da melhor maneira.
Gbjbaanb

1

Por causa da perspectiva ... A melhor prática é ter um único commit por <<issue-management-system>>problema, se possível.

Você pode ter quantos commits quiser em seu próprio ramo / repo, mas é seu histórico relevante para o que você está fazendo agora para a SUA perspectiva ... não é a HISTÓRIA para toda a EQUIPE / PROJETO ou aplicativo de seu perspectiva a ser mantida daqui a vários meses ...

Portanto, sempre que você desejar confirmar uma correção ou recurso de bug em um repositório comum (este exemplo é com o ramo de desenvolvimento), faça o seguinte:

como refazer sua ramificação de recursos para desenvolver rapidamente

    # set your current branch , make a backup of it , caveat minute precision
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # squash all your changes at once 
    git reset $(git merge-base develop $curr_branch)

    # check the modified files to add 
    git status

    # add the modified files dir by dir, or file by file or all as shown
    git add --all 

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # add the single message of your commit for the stuff you did 
    git commit -m "<<MY-ISSUE-ID>>: add my very important feature"

    # check once again 
    git log --format='%h %ai %an %m%m %s'  | less

    # make a backup once again , use seconds precision if you were too fast ... 
    curr_branch=$(git rev-parse --abbrev-ref HEAD); git branch "$curr_branch"--$(date "+%Y%m%d_%H%M"); git branch -a | grep $curr_branch | sort -nr

    # compare the old backup with the new backup , should not have any differences 
    git diff <<old-backup>>..<<new-backup-branch>>


    # you would have to git push force to your feature branch 
    git push --force 

isso nem tenta responder à pergunta: por que o squash git se compromete com solicitações pull? Veja como responder
gnat

após a explicação tentar fazê-lo deve fazê-lo ...
Yordan Georgiev

De alguma forma, falho em ver como as explicações adicionadas oferecem algo substancial sobre os pontos levantados e explicados nas principais respostas votadas que foram postadas há vários anos (embora seja apreciado o esforço de melhorar a primeira revisão) #
1013

a palavra-chave é a "perspectiva", o conceito de perspectiva facilita a explicação desse tipo de problema em TI, por exemplo - sua perspectiva para a resposta desta pergunta é a que já está sendo fornecida, minha perspectiva está usando a palavra-chave "perspectiva" e fornecendo um exemplo prático de como fazer implementar o mesmo melhores práticas explicou através de diferentes perspectivas ...
Yordan Georgiev

1

Gostaria de ver se o código está sendo público ou não.

Quando os projetos permanecem privados:

Eu recomendaria não esmagar e ver a confecção da lingüiça. Se você usa confirmações boas e pequenas, ferramentas como o git bisect são super úteis e as pessoas podem identificar rapidamente as confirmações de regressão e ver por que você fez isso (por causa da mensagem de confirmação).

Quando os projetos se tornam públicos:

Esmague tudo porque alguns commits podem conter vazamentos de segurança. Por exemplo, uma senha que foi confirmada e removida novamente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.