A pergunta que todo o programador acaba por fazer
No mês passado funcionava. Tem a certeza. A sidebar estava alinhada, o parser de datas tratava dos fusos horários, o botão tinha o tom certo de azul. Depois chega um relatório de bug, e agora está a olhar para um ficheiro com dois anos de histórico, a tentar responder a uma pergunta enganadoramente simples: quando é que isto mudou, e que commit é o responsável?
Logo a seguir vem a segunda pergunta: quem fez essa alteração, e porquê? A mensagem do commit pode explicá-lo. O autor pode lembrar-se. Mas primeiro tem de encontrar o commit, e é aí que começa a ginástica mental.
A caixa de ferramentas da CLI, e onde ela fica aquém
O Git já tem tudo o que precisa para investigar o histórico de um ficheiro. As ferramentas são poderosas e comprovadas -- a fricção não está em nenhum comando isolado, mas na quantidade deles que tem de encadear.
git log --follow
git log --follow --oneline -- src/styles/sidebar.css
Isto lista todos os commits que tocaram no ficheiro, mesmo através de renomeações. É o ponto de partida certo. Mas devolve SHAs e mensagens de commit, não conteúdo. Para ver como o ficheiro estava realmente num dado momento, precisa do comando seguinte.
git show SHA:path
git show a3b4c5d:src/styles/sidebar.css
Isto imprime o ficheiro completo tal como existia nesse commit. Para comparar duas versões, execute-o duas vezes e faça o diff do resultado, ou use git diff a3b4c5d 9f2e1d0 -- src/styles/sidebar.css. Agora repita isso para cada commit candidato. Se trinta commits tocaram no ficheiro, vai passar um bom bocado a copiar e colar SHAs, mantendo na cabeça, o tempo todo, as diferenças entre versões.
git blame
git blame src/styles/sidebar.css
O blame anota cada linha com o último commit que a modificou. É excelente quando a linha suspeita ainda está no ficheiro e quer saber quem a escreveu. É menos útil quando a regressão veio de uma linha que foi apagada, ou quando a alteração mais recente a uma linha é uma reformatação inofensiva que esconde a alteração que realmente lhe interessa. Aí dá por si a executar git blame SHA^ -- path vez após vez, descascando o histórico uma camada de cada vez.
git bisect
O bisect executa uma pesquisa binária por todo o seu histórico para encontrar o commit que quebrou um comportamento testável. Quando "quebrado" significa que a build falha ou um teste fica vermelho, o bisect é exatamente a ferramenta certa, e nada numa GUI o substitui. Mas é maquinaria pesada para uma pergunta como "quando é que esta regra CSS mudou?". Não precisa de fazer checkout e recompilar o projeto a cada passo -- só quer olhar para um ficheiro ao longo do tempo.
Portanto, a resposta da CLI a "ver este ficheiro evoluir" é: listar os commits com log, imprimir cada versão com show, fazer o diff dos pares à mão, e manter todo o estado na cabeça. Cada comando funciona. O problema é o workflow.
A timeline do ficheiro: uma resposta visual
O GitSquid lançou a timeline do ficheiro na v2.6, e está incluída no plano Free. A ideia é simples: em vez de reconstruir a evolução de um ficheiro commit a commit na sua cabeça, vê-a acontecer no ecrã.
Abra-a com um clique direito
Clique com o botão direito em qualquer ficheiro e escolha Mostrar histórico. A vista de histórico do ficheiro abre-se, focada inteiramente nesse único ficheiro.
Uma marca por commit
Uma faixa de timeline fica acima do visualizador do ficheiro: uma marca para cada commit que tocou no ficheiro. A vida inteira do ficheiro, disposta na horizontal. Um ficheiro com três commits parece calmo; um ficheiro com sessenta marcas diz-lhe algo sobre o seu churn antes de ter lido uma única linha.
Percorra as versões
Arraste o slider e o visualizador atualiza-se enquanto arrasta. Este é o núcleo da funcionalidade: não está a saltar entre snapshots discretos que tem de comparar mentalmente -- está a ver o ficheiro mudar. Percorrer para trás e para a frente parece instantâneo depois da primeira passagem, por isso, uma vez atravessado o histórico, pode oscilar livremente à volta de uma zona suspeita, afunilando até ao commit exato onde as coisas mudaram.
Passe o rato para ver quem mudou o quê
Passe o rato sobre qualquer marca e obtém o autor e a informação do commit para esse ponto do histórico. A pergunta "quem escreveu isto e quando" fica respondida sem sair da timeline, e sem executar o blame de todo.
Carregue em play
Clique em ▶ e o GitSquid reproduz automaticamente o histórico. Para uma primeira passagem por um ficheiro desconhecido, esta é a forma mais rápida de ganhar intuição: vê estruturas a aparecer, a crescer, a serem refatoradas. É a diferença entre ler trinta diffs e ver um time-lapse.
Snapshot ou Diff
Um alternador Snapshot / Diff no cabeçalho troca entre duas vistas: o conteúdo completo do ficheiro tal como existia nesse commit, ou o clássico diff ficheiro-contra-pai. O modo Snapshot responde a "como é que o ficheiro estava nessa altura?"; o modo Diff responde a "o que é que este commit mudou exatamente?". A caça às regressões normalmente começa em modo Snapshot e termina em modo Diff.
Salte para uma versão
Depois de encontrar a versão que lhe interessa, três ações à direita levam-no mais longe: Copiar SHA coloca o hash do commit na sua área de transferência para usar em qualquer lado, Abrir commit salta para o commit completo para poder ver o que mais mudou junto com o seu ficheiro, e Checkout detached coloca o seu diretório de trabalho exatamente nesse commit quando precisa de executar o código antigo a sério.
Passo a passo: encontrar uma regressão de CSS
Um exemplo concreto. A sidebar da sua aplicação perdeu o padding, e ninguém sabe quando. Eis a caça com a timeline do ficheiro:
- Clique direito em
sidebar.css→ Mostrar histórico. A timeline mostra 24 marcas ao longo de oito meses. - Percorra do mais recente para o mais antigo enquanto observa a regra do padding. Por volta dos dois terços, a regra muda visivelmente. Depois da primeira passagem, o percurso é instantâneo, por isso mexe o slider para trás e para a frente à volta dessa zona até isolar a marca exata onde o valor mudou.
- Passe o rato sobre a marca. Foi um colega de equipa, há três semanas, num commit rotulado como refatoração de layout. Mistério meio resolvido.
- Mude para Diff. O alternador mostra o diff ficheiro-contra-pai: o padding foi condensado numa propriedade shorthand, e um valor perdeu-se na tradução. Aí está a regressão.
- Abrir commit. O commit completo revela que tocou em doze ficheiros como parte de uma limpeza geral -- o tipo de alteração fácil de rever com pouca atenção. Se a sua equipa debate como alterações destas devem entrar no histórico, em primeiro lugar, veja rebase vs merge.
- Copie o SHA e corrija. Com o commit culpado identificado, pode fazer revert, corrigir em frente, ou fazer cherry-pick à volta dele -- o nosso guia sobre como desfazer um commit Git explica que opção serve para cada situação.
Tempo total: uns dois minutos, nenhum SHA memorizado, nenhuma arqueologia no scrollback do terminal.
Quando o blame ou o bisect continua a ser a ferramenta certa
A timeline não substitui o resto da caixa de ferramentas, e fingir o contrário seria desonesto:
- Use o blame quando já está a olhar para uma linha específica e quer o seu último autor ali mesmo. Para uma única linha que não foi reformatada recentemente, o blame continua a ser o caminho mais curto.
- Use o bisect quando o sintoma é "a build partiu" ou "um teste falha" e não sabe que ficheiro é o responsável. O bisect pesquisa o histórico do repositório inteiro; a timeline examina um ficheiro. Respondem a perguntas diferentes.
- Use a timeline quando a pergunta é sobre a evolução de um ficheiro: quando é que isto mudou, quem o mudou, e como é que estava antes.
Veja o seu próprio código evoluir
A timeline do ficheiro vem incluída no plano Free do GitSquid -- sem conta, sem telemetria, nativa em macOS, Windows e Linux. Abra um repositório, clique com o botão direito num ficheiro sobre o qual sempre teve curiosidade, e carregue em play.
Descarregue o GitSquid Free e dê à sua próxima caça à regressão uma timeline em vez de uma lista de SHAs.