git diff $sha1..$sha1^
produz um patch que reverte o $sha1
commit (lista as diferenças entre o commit e seu pai). Se $file
for especificado, ele limitará esse patch às alterações feitas em $file
no commit fornecido.
Esse patch é então alimentado para patch -p1
, que removerá os nomes de diretório falsos usados por git
( a/
e b/
) e tentará aplicar o patch a quaisquer arquivos listados no patch ( ie , $file
se foi nomeado e alterado no commit fornecido, ou todos os arquivos alterados no commit fornecido, incluindo arquivos em subdiretórios). Se os arquivos presentes no diretório atual e em seus subdiretórios forem substancialmente diferentes (ou por extensão, ausentes), patch
não aplicará o patch.
Isso é possível devido ao fato de que os patches em formato unificado, conforme produzidos por git diff
(e diff -u
), incluem os nomes dos arquivos que estão sendo corrigidos e o contexto do patch. Veja um exemplo (não de git
, mas mostra a ideia):
diff -ur cli-common-0.9+nmu1.orig/policy-remove cli-common-0.9+nmu1/policy-remove
--- cli-common-0.9+nmu1.orig/policy-remove 2015-02-25 21:34:08.000000000 +0100
+++ cli-common-0.9+nmu1/policy-remove 2017-04-08 20:47:09.029065259 +0200
@@ -11,4 +11,4 @@
#echo "Removing GAC policy file ($POLICY) from available GACs"
/usr/share/cli-common/gac-package-remove $POLICY > /dev/null
-rm /usr/share/cli-common/packages.d/$POLICY.installcligac
+rm -f /usr/share/cli-common/packages.d/$POLICY.installcligac
Este patch diz que está modificando o arquivo chamado cli-common-0.9+nmu1.orig/policy-remove
para produzir o arquivo chamado cli-common-0.9+nmu1/policy-remove
. A alteração começa na linha 11 e abrange 4 linhas, incluindo contexto (esse é o @@ -11,4
); no destino, as linhas alteradas estão na mesma posição ( +11,4 @@
). Existem três linhas de contexto acima da mudança, depois a alteração em si, excluindo uma linha que começa com rm
e adicionando uma linha começando com rm -f
. Quando patch
aplicar isso, ele procurará pelo arquivo com o nome apropriado (depois de remover os componentes do caminho, se instruído com uma opção -p
), e compara o contexto no arquivo com o patch; somente se o contexto corresponder (dentro de algumas linhas, dependendo das opções de fuzz) a mudança será aplicada.
O objetivo deste script é tentar reverter as alterações feitas em um único arquivo no commit fornecido (daí seu nome). Se isso é possível ou não, depende das alterações feitas no arquivo desde o commit; mas muitas vezes é bastante útil na prática. (Para reverter um commit completo, use git revert
).