RecursosPreçosDocumentaçãoBlog
Obtenha mote
← Blog
Engenharia·7 de setembro de 2026·4 min de leitura

Para que o documento não fique vazio após um git checkout, observamos os bytes, não o relógio

Você já trocou de branch e o documento que estava aberto ficou em branco? Estávamos tirando capturas de tela para publicar no blog quando vimos essa cena no nosso aplicativo e, aproveitando a correção, resolvemos também um bug mais silencioso.

m
mote
O banner exibido quando um documento em edição é alterado fora do aplicativo. Ele permite escolher entre recarregar o arquivo ou continuar editando, enquanto a barra de status mostra que ainda restam 13 B não salvos.
O banner exibido quando um documento em edição é alterado fora do aplicativo. Ele permite escolher entre recarregar o arquivo ou continuar editando, enquanto a barra de status mostra que ainda restam 13 B não salvos.

O mote confia nos arquivos exatamente como eles são. O conteúdo exibido na tela é igual aos bytes no disco e, ao salvar, ele também grava esses bytes sem alterações.

Mas o que acontece quando um arquivo é alterado fora do aplicativo? Se o mote não perceber, no próximo salvamento ele poderá sobrescrever o conteúdo escrito por outra pessoa.

Descobrimos esse problema enquanto tirávamos capturas de tela. Ao alternar entre branches para preparar a tela de “bytes que mudam”, o documento às vezes ficava travado em branco.

Dois problemas escondidos por trás da tela em branco

O git checkout não altera os arquivos de uma só vez. Ele apaga o arquivo existente, cria um novo e depois grava o conteúdo.

O monitor de arquivos informa esse processo por meio de vários eventos. Mas o mote lia o arquivo assim que recebia o primeiro evento. Como o conteúdo ainda não havia sido preenchido, às vezes ele lia um arquivo vazio.

Ao reler o arquivo, ele também recriava o monitor. Nesse intervalo, os eventos restantes no monitor anterior desapareciam, e a tela ficava travada em branco.

Havia também um problema mais perigoso.

Depois de salvar, o mote tratava os eventos recebidos durante 1,5 segundo como “alterações feitas por mim” e os ignorava. Mas, nesse intervalo, um formatador ou um hook de salvamento também poderia modificar o arquivo novamente.

Na prática, se o arquivo fosse alterado 0,3 segundo depois de ser salvo, o mote não mostrava nenhuma mudança. Se o usuário salvasse novamente, o conteúdo alterado externamente desaparecia silenciosamente.

Um era visível, e o outro não. A causa era a mesma.

As alterações nos arquivos estavam sendo avaliadas pelo tempo, e não pelo conteúdo.

Passamos a comparar o conteúdo em vez do tempo

Agora, quando chega um evento de arquivo, ele não é lido imediatamente. Esperamos 200ms e reiniciamos a espera sempre que chega um novo evento.

Assim, o arquivo é lido apenas uma vez, em seu estado final, depois que o processo de apagar, criar e gravar termina. Temporizadores antigos são invalidados por um número de geração. Também não criamos um temporizador que continue rodando quando nada está acontecendo.

O conteúdo lido é comparado com os últimos bytes do disco vistos pelo buffer.

Se forem iguais, significa que o aplicativo salvou o arquivo ou que o mesmo conteúdo foi gravado novamente. Se forem diferentes, trata-se de uma alteração externa real. Não importa se passaram 0,1 segundo ou 1 segundo desde o salvamento.

Ao reler o mesmo arquivo, também mantemos o monitor. Assim, os eventos não desaparecem enquanto o monitor está sendo trocado.

Só perguntamos quando há uma edição em andamento

Depois de detectar uma alteração externa, verificamos o estado do usuário.

Se houver edições não salvas, mostramos um banner. O usuário pode escolher entre recarregar o arquivo alterado ou continuar editando o conteúdo atual. Como há conteúdo que pode ser perdido em qualquer uma das opções, o aplicativo não toma essa decisão pelo usuário.

Se não houver uma edição em andamento, o novo conteúdo é carregado imediatamente. O banner não é exibido, e as posições do cursor e da rolagem são preservadas. Ao alternar entre branches enquanto se lê um documento, é mais natural manter o ponto onde a leitura parou do que mostrar uma notificação.

Arquivo alterado externamente quando não havia uma edição em andamento. O conteúdo foi atualizado sem exibir um banner, e o indicador de edição no título e os bytes pendentes na barra de status desapareceram.
Arquivo alterado externamente quando não havia uma edição em andamento. O conteúdo foi atualizado sem exibir um banner, e o indicador de edição no título e os bytes pendentes na barra de status desapareceram.

Também não tomamos uma decisão imediata quando o arquivo desaparece. Como outro editor pode estar renomeando o arquivo, verificamos novamente após 300ms.

Se o arquivo ainda não existir nesse momento, avisamos o usuário. O conteúdo que estava sendo escrito permanece no buffer e, ao salvar novamente, o arquivo é criado.

Verificamos seis situações no ambiente real

É difícil verificar esse problema apenas com testes unitários. Isso acontece porque ele surge quando o monitor de arquivos, o disco real e o gerenciador de janelas trabalham em conjunto.

Por isso, criamos verificações executadas em um ambiente real.

Verificamos se o buffer acompanha o arquivo mesmo depois de trocar de branch dez vezes e se uma alteração feita no arquivo 300ms após o salvamento também é refletida. Também verificamos se nenhuma notificação aparece quando o próprio aplicativo salva o arquivo e se, quando ocorre uma alteração externa durante a edição, o banner é exibido sem perder o conteúdo que estava sendo escrito.

São seis verificações ao todo. Nós as executamos a cada lançamento.

Saída da execução de tools/e2e/external.sh em uma tela física. Todas as seis linhas mostram ok, e a última linha mostra PASS=6 FAIL=0.
Saída da execução de tools/e2e/external.sh em uma tela física. Todas as seis linhas mostram ok, e a última linha mostra PASS=6 FAIL=0.

Tratar o arquivo como a fonte da verdade não termina em gravar exatamente o conteúdo salvo.

Quando um arquivo muda sem que saibamos, também precisamos garantir que essa alteração não passe despercebida.

← AnterioresComo evitamos que o arquivo seja corrompido mesmo se o aplicativo fechar durante o salvamento
Acompanhe por RSS.
Deixe só a escrita.
Português (Brasil)
© 2026 mote