Acabei de ler a alteração de um único arquivo em um commit anterior no git, mas infelizmente a solução aceita 'reordena' os commits, que não é o que eu quero. Então aqui está minha pergunta:
De vez em quando, percebo um bug no meu código enquanto trabalho em um recurso (não relacionado). Um rápido git blame
então revela que o bug foi introduzido há alguns commits atrás (eu faço vários commits, então geralmente não é o commit mais recente que introduziu o bug). Nesse ponto, geralmente faço o seguinte:
git stash # temporarily put my work aside
git rebase -i <bad_commit>~1 # rebase one step before the bad commit
# mark broken commit for editing
vim <affected_sources> # fix the bug
git add <affected_sources> # stage fixes
git commit -C <bad_commit> # commit fixes using same log message as before
git rebase --continue # base all later changes onto this
No entanto, isso acontece com tanta frequência que a sequência acima está ficando irritante. Especialmente o 'rebase interativo' é enfadonho. Existe algum atalho para a sequência acima, o que me permite corrigir um commit arbitrário no passado com as mudanças encenadas? Estou perfeitamente ciente de que isso muda a história, mas estou cometendo erros com tanta frequência que adoraria ter algo como
vim <affected_sources> # fix bug
git add -p <affected_sources> # Mark my 'fixup' hungs for staging
git fixup <bad_commit> # amend the specified commit with staged changes,
# rebase any successors of bad commit on rewritten
# commit.
Talvez um script inteligente que pode reescrever commits usando ferramentas de encanamento ou algo assim?
rebase -i
?rebase --onto tmp bad-commit master
. Conforme está escrito, ele tentará aplicar o commit incorreto ao estado de commit fixo.Respostas:
RESPOSTA ATUALIZADA
Um tempo atrás, um novo
--fixup
argumento foi adicionado aogit commit
qual pode ser usado para construir um commit com uma mensagem de log adequada paragit rebase --interactive --autosquash
. Portanto, a maneira mais simples de corrigir um commit anterior é agora:RESPOSTA ORIGINAL
Aqui está um pequeno script Python que escrevi há algum tempo, que implementa essa
git fixup
lógica que esperava na minha pergunta original. O script assume que você testou algumas mudanças e depois as aplica ao commit fornecido.NOTA : Este script é específico do Windows; ele procura
git.exe
e define aGIT_EDITOR
variável de ambiente usandoset
. Ajuste isso conforme necessário para outros sistemas operacionais.Usando este script, posso implementar precisamente o fluxo de trabalho 'consertar fontes quebradas, preparar correções, executar git fixup' que solicitei:
fonte
git stash
e emgit stash pop
torno de seu rebase para não precisar mais de um diretório de trabalho limpogit stash
egit stash pop
: você está certo, mas infelizmentegit stash
é muito mais lento no Windows do que no Linux ou OS / X. Como meu diretório de trabalho geralmente está limpo, omiti esta etapa para não desacelerar o comando.git rebase -i --fixup
, e ele foi baseado no commit corrigido como ponto de partida, então o argumento sha não foi necessário no meu caso.git config --global rebase.autosquash true
O que eu faço é:
Seu editor abrirá com uma lista dos últimos 5 commits, prontos para serem mexidos. Mudança:
...para:
Salve e saia do seu editor, e a correção será comprimida de volta no commit a que pertence.
Depois de fazer isso algumas vezes, você fará em segundos enquanto dorme. Rebasing interativo é o recurso que realmente me vendeu no git. É incrivelmente útil para isso e muito mais ...
fonte
--autosquash
opção paragit rebase
, que reordena automaticamente as etapas no editor para você. Veja minha resposta para um script que aproveita isso para implementar umgit fixup
comando.Um pouco tarde para a festa, mas eis uma solução que funciona como o autor imaginou.
Adicione ao seu .gitconfig:
Exemplo de uso:
No entanto, se você tiver mudanças não planejadas, deve armazená-las antes do rebase.
Você pode modificar o alias para esconder automaticamente, em vez de dar um aviso. No entanto, se a correção não se aplicar corretamente, você precisará abrir o estoque manualmente após corrigir os conflitos. Salvar e abrir manualmente parece mais consistente e menos confuso.
fonte
git fixup HEAD
é para isso que criei um alias. Eu também poderia usar emendar para isso, suponho.amend = commit --amend --reuse-message=HEAD
Então você pode simplesmente digitargit amend
ougit amend -a
e pular o editor da mensagem de confirmação.Para consertar um commit:
onde a0b1c2d3 é o commit que você deseja consertar e onde 2 é o número de commits +1 colados que você deseja alterar.
Nota: git rebase --autosquash sem -i não funcionou, mas com -i funcionou, o que é estranho.
fonte
--autosquash
sem-i
ainda não funciona.EDITOR=true git rebase --autosquash -i
ATUALIZAÇÃO: uma versão mais limpa do script agora pode ser encontrada aqui: https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup .
Tenho procurado por algo semelhante. No entanto, este script Python parece muito complicado, portanto, montei minha própria solução:
Primeiro, meus aliases git são assim (emprestados daqui ):
Agora a função bash se torna bastante simples:
Este código primeiro prepara todas as alterações atuais (você pode remover esta parte, se desejar preparar os arquivos você mesmo). Em seguida, cria o commit de correção (squash também pode ser usado, se for necessário). Depois disso, ele inicia um rebase interativo com o
--autosquash
sinalizador no pai do commit que você fornece como argumento. Isso abrirá seu editor de texto configurado, para que você possa verificar se tudo está conforme o esperado e simplesmente fechar o editor encerrará o processo.A
if [[ "$1" == HEAD* ]]
parte (emprestada daqui ) é usada, porque se você usar, por exemplo, HEAD ~ 2 como sua referência de commit (o commit que você deseja corrigir as alterações atuais), então o HEAD será deslocado após o commit de correção ter sido criado e você precisaria usar HEAD ~ 3 para se referir ao mesmo commit.fonte
Você pode evitar o estágio interativo usando um editor "nulo":
Isso será usado
/bin/true
como editor, em vez de/usr/bin/vim
. Ele sempre aceita qualquer sugestão do git, sem avisar.fonte
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i" ...
).O que realmente me incomodou sobre o fluxo de trabalho de correção foi que eu tinha que descobrir sozinho em qual commit eu queria esmagar a mudança todas as vezes. Eu criei um comando "git fixup" que ajuda com isso.
Este comando cria commits de correção, com a mágica adicional de usar git-deps para encontrar automaticamente o commit relevante, então o fluxo de trabalho geralmente se resume a:
Isso só funciona se as mudanças preparadas puderem ser atribuídas sem ambigüidade a um commit particular na árvore de trabalho (entre o master e o HEAD). Acho que esse é o caso com muita frequência para o tipo de pequenas alterações para as quais uso isso, por exemplo, erros de digitação em comentários ou nomes de métodos recém-introduzidos (ou renomeados). Se este não for o caso, pelo menos exibirá uma lista de commits de candidatos.
Eu uso muito isso em meu fluxo de trabalho diário, para integrar rapidamente pequenas alterações em linhas alteradas anteriormente em commits em meu branch de trabalho. O roteiro não é tão bonito quanto poderia ser e é escrito em zsh, mas tem feito o trabalho para mim bem o suficiente por um bom tempo que nunca senti a necessidade de reescrevê-lo:
https://github.com/Valodim/git-fixup
fonte
Você pode criar uma correção para um arquivo específico usando este alias.
Se você fez algumas alterações,
myfile.txt
mas não deseja colocá-las em um novo commit,git fixup-file myfile.txt
irá criar umfixup!
para o commit ondemyfile.txt
foi modificado pela última vez, e então irárebase --autosquash
.fonte
git rebase
não seja chamado automaticamente.commit --fixup
erebase --autosquash
são ótimos, mas não fazem o suficiente. Quando eu tenho uma sequência de commitsA-B-C
e escrevo mais algumas mudanças em minha árvore de trabalho que pertencem a um ou mais desses commits existentes, eu tenho que olhar manualmente o histórico, decidir quais mudanças pertencem a quais commits, encená-los e criar ofixup!
compromete. Mas git já tem acesso a informações suficientes para fazer tudo isso por mim, então escrevi um script Perl que faz exatamente isso.Para cada pedaço no
git diff
script, usegit blame
para encontrar o commit que tocou por último nas linhas relevantes e chamadasgit commit --fixup
para escrever osfixup!
commits apropriados , essencialmente fazendo a mesma coisa que fazia manualmente antes.Se você achar que é útil, sinta-se à vontade para aprimorá-lo e iterar nele e talvez um dia possamos obter esse recurso de maneira
git
adequada. Eu adoraria ver uma ferramenta que pode entender como um conflito de mesclagem deve ser resolvido quando ele foi introduzido por um rebase interativo.fonte
Eu escrevi uma pequena função shell chamada
gcf
para executar o commit de correção e o rebase automaticamente:Por exemplo, você pode corrigir o segundo commit antes do último com:
gcf HEAD~~
Aqui está a função . Você pode colá-lo em seu
~/.bashrc
Ele usa
--autostash
para esconder e exibir quaisquer alterações não confirmadas, se necessário.--autosquash
requer um--interactive
rebase, mas evitamos a interação usando um manequimEDITOR
.--no-fork-point
protege os commits de serem silenciosamente descartados em raras situações (quando você bifurcou um novo branch, e alguém já rebasou commits anteriores).fonte
Não conheço uma forma automatizada, mas aqui está uma solução que pode ser mais fácil de ser botada por humanos:
fonte
git fixup
comando.--autosquash
Eu recomendaria https://github.com/tummychow/git-absorb :
fonte