Revertendo um único arquivo para uma versão anterior no git

Existe uma maneira de passar por diferentes compromissos em um arquivo. Digamos que eu modifiquei um arquivo 5 vezes e quero voltar a modificar 2, depois de já ter cometido e empurrado para um repositório.

No meu entender, a única maneira é manter muitos ramos, não é assim? Se eu'estou certo eu'vou ter centenas de ramos em poucos dias, então eu'provavelmente não estou entendendo isso realmente.

Alguém pode esclarecer isso, por favor?

Solução

Vamos's começar com uma descrição qualitativa do que queremos fazer (muito disto é dito na resposta de Ben Straub's). Nós'fizemos alguns commits, cinco dos quais mudaram um determinado arquivo, e queremos reverter o arquivo para uma das versões anteriores. Primeiro de tudo, git doesn't manter números de versão para arquivos individuais. Ele apenas rastreia o conteúdo - um commit é essencialmente um instantâneo da árvore de trabalho, juntamente com alguns metadados (por exemplo, mensagem de commit). Então, nós temos que saber qual commit tem a versão do arquivo que nós queremos. Uma vez que sabemos isso, nós'precisaremos fazer um novo commit revertendo o arquivo para esse estado. (Nós podemos'não nos limitarmos a mexer na história, porque nós'já empurrámos este conteúdo, e editar a história mexe com todos os outros).

Então que's comecem por encontrar o compromisso certo. Você pode ver os commits que fizeram modificações em determinado(s) arquivo(s) muito facilmente:

git log path/to/file

Se suas mensagens de commit são'não são suficientemente boas, e você precisa ver o que foi feito ao arquivo em cada commit, use a opção `-p/--patch':

git log -p path/to/file

Ou, se preferir a vista gráfica de gitk

gitk path/to/file

Você também pode fazer isso assim que você'tiver iniciado o gitk através do menu view; uma das opções para uma view é uma lista de caminhos a serem incluídos.

De qualquer forma, você'será capaz de encontrar o SHA1 (hash) do commit com a versão do arquivo que você quer. Agora, tudo o que você tem que fazer é isto:

# get the version of the file from the given commit
git checkout  path/to/file
# and commit this modification
git commit

(O comando checkout primeiro lê o arquivo no índice, depois o copia para a árvore de trabalho, então lá'não há necessidade de usar git add para adicioná-lo ao índice em preparação para a submissão).

Se o seu arquivo pode não ter um histórico simples (por exemplo, renomes e cópias), veja o excelente comentário de VonC's. O git pode ser direcionado para procurar mais cuidadosamente por tais coisas, às custas da velocidade. Se você'está confiante na história'é simples, você precisa'não se preocupe.

Comentários (11)

Git doesn't pensa em termos de versões de arquivos. Uma versão em git é um instantâneo de toda a árvore.

Dado isto, o que você realmente quer é uma árvore que tenha o conteúdo mais recente da maioria dos arquivos, mas com o conteúdo de um arquivo o mesmo que era 5 commits atrás. Isto tomará a forma de um novo commit em cima dos antigos, e a última versão da árvore terá o que você quer.

Eu não'não sei se existe'é uma linha única que irá reverter um único arquivo para o conteúdo de 5 commits atrás, mas a solução lo-fi deve funcionar: checkout master~5, copiar o arquivo em outro lugar, checkout master, copiar o arquivo de volta, depois commit.

Comentários (2)

Você pode fazer uma diferença que desfaça as mudanças que você quer e comprometer-se com isso.

Por exemplo, se você quiser desfazer as mudanças no intervalo 'de...até', faça o seguinte

git diff to..from > foo.diff  # get a reverse diff
patch < foo.diff
git commit -a -m "Undid changes from..to".
Comentários (5)