¿Cómo sustituir texto de archivos en el historial de git?


Siempre he usado un cliente git basado en la interfaz (SmartGit) y por lo tanto no tengo mucha experiencia con la consola git.

Sin embargo, ahora me enfrento a la necesidad de sustituir una cadena en todo .archivos txt del historial (por lo tanto, no borrando todo el archivo, sino simplemente sustituyendo una cadena). Encontré el siguiente comando:

git filter-branch --tree-filter 'git ls-files -z "*.php" |xargs -0 perl -p -i -e "s#(PASSWORD1|PASSWORD2|PASSWORD3)#xXxXxXxXxXx#g"' -- --all

Probé esto, y desafortunadamente noté que mientras que la contraseña se cambió, todos los archivos binarios se corrompieron. Imágenes, etc. todos estarían corrompidos.

Es ¿hay una mejor manera de hacer esto que no corrompa mis archivos binarios?

Gracias.

EDITAR:

Me mezclé con algo. El código real que causó que los archivos binarios se corrompieran fue:

$ git filter-branch --tree-filter "find . -type f -exec sed -i -e 's/originalpassword/newpassword/g' {} \;"

El código en la parte superior en realidad eliminado todos los archivos con mi contraseña curiosamente.

Author: Roberto Tyley, 2010-11-06

4 answers

Puede evitar tocar archivos no deseados pasando -name "pattern" a find.

Esto funciona para mí:

git filter-branch --tree-filter "find . -name '*.php' -exec sed -i -e \
    's/originalpassword/newpassword/g' {} \;"
 31
Author: jweyrich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-06 17:04:27

Recomiendo usar el BFG Repo-Cleaner, una alternativa más simple y rápida a git-filter-branch diseñada específicamente para reescribir archivos del historial de Git.

Debe seguir cuidadosamente estos pasos aquí: https://rtyley.github.io/bfg-repo-cleaner/#usage - pero el bit central es solo esto: descargue el jar de BFG (requiere Java 7 o superior) y ejecute este comando:

$ java -jar bfg.jar  --replace-text replacements.txt -fi *.php  my-repo.git

El archivo replacements.txt debe contener todas las sustituciones que desee hacer, en un formato como este (una entrada por línea - tenga en cuenta que los comentarios no deben incluirse):

PASSWORD1 # Replace literal string 'PASSWORD1' with '***REMOVED***' (default)
PASSWORD2==>examplePass         # replace with 'examplePass' instead
PASSWORD3==>                    # replace with the empty string
regex:password=\w+==>password=  # Replace, using a regex
regex:\r(\n)==>$1               # Replace Windows newlines with Unix newlines

Se escaneará todo el historial del repositorio, y los archivos .php (de menos de 1 MB de tamaño) tendrán las sustituciones realizadas: cualquier cadena coincidente (que no esté en su última confirmación) será reemplazada.

Revelación completa: Soy el autor del Repo-Cleaner de BFG.

 63
Author: Roberto Tyley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-28 21:06:45

He creado un archivo en /usr/local/git/findsed.sh , con el siguiente contenido:

find . -name 'githubDirToSubmodule.sh' -exec sed -i '' -e 's/What I want to remove//g' {} \;

Ejecuté la orden:

git filter-branch --tree-filter "sh /usr/local/git/findsed.sh"

Explicación de comandos

Cuando ejecuta git filter-branch, esto pasa por cada revisión que haya enviado, una por una. -- árbol-filtro ejecuta el findsed.sh script en cada revisión confirmada, lo guarda, luego avanza a la siguiente revisión.

El comando find encuentra un archivo o conjunto de archivos específicos y ejecuta (- exec) el sed editor en ese archivo. sed es un comando que toma la expresión regular después de s / y la reemplaza con la cadena entre /y / g (en blanco en mi ejemplo). {} es una referencia a la ruta de los archivos que fue dada por el comando find. La ruta del archivo se envía a sed, para que sed sepa en qué trabajar. \ ; acaba de terminar la orden-exec.

Separar el script de shell y el comando en piezas separadas permite menos complicaciones cuando se trata de comillas " o "".

Peculiaridades

I implementado con éxito esto en un mac, y al parecer sed es un particular(mayor?) versión en macs. Esto importa, ya que a veces se comporta de manera diferente. Asegúrese de hacer sed-i " o de lo contrario estaba agregando una "-e" al final de los archivos, pensando que eso era lo que quería nombrar mis archivos de copia de seguridad. - i " dice no hacer archivos de copia de seguridad, solo edite los archivos en su lugar y no se necesita ningún archivo de copia de seguridad.

Especificando-name "filename.sh" me ayudó a evitar otro problema que no podía resolver. Había otro archivo con .sh y ese archivo terminó sin un carácter de nueva línea. sed por alguna razón, añadiría un carácter de nueva línea al final, a pesar de que el 's/bla/bla/g' no coincide con nada en ese archivo. Así que en lugar de resolver ese problema, solo le dije al find que ignorara todos los demás archivos.

Comandos Adicionales que funcionan

Además, encontré estos comandos para trabajar en el findsed.sh archivo (solo una orden a la vez, no multple, así que comenta # los otros fuera):

find . -name '.publishNewZenPackFromGithub.sh.swp' -exec rm -f {} \;
find . -name '*' -exec grep -H PassToRemove {} \;

Disfrute!

 4
Author: Nay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-03 18:57:38

Podría ser un problema de expansión del shell. Si filter-branch está perdiendo las comillas alrededor de "*.php" en el momento en que evalúa el comando, puede estar expandiéndose a nada, por lo tanto git ls-files -z listando todos los archivos.

Puedes revisar la fuente de la rama de filtro o probar diferentes trucos de cita, pero lo que haría es simplemente hacer un script de shell de una línea que haga tu filtro de árbol y pasar ese script en su lugar.

 1
Author: Ben Jackson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-05 22:56:52