2009-05-16 10 views
267

Me gustaría poner un proyecto Git en GitHub pero contiene ciertos archivos con datos confidenciales (nombres de usuario y contraseñas, como /config/deploy.rb para capistrano).Eliminar archivos confidenciales y sus confirmaciones del historial de Git

Sé que puedo agregar estos nombres de archivo a .gitignore, pero esto no eliminaría su historial dentro de Git.

Tampoco quiero volver a empezar eliminando el directorio /.git.

¿Hay alguna manera de eliminar todas las huellas de de un archivo en particular en el historial de Git?

+0

Relacionados: [¿Cómo eliminar/eliminar un archivo grande del historial de confirmaciones en el repositorio de Git?] (Http://stackoverflow.com/questions/2100907/how-do-i-purge-a-huge-file- from-commits-in-git-history) y [Eliminar completamente el archivo de todo el historial de confirmaciones del repositorio de Git] (http://stackoverflow.com/questions/307828/completely-remove-unwanted-file-from-git-repository-history) –

Respuesta

349

Para todos los efectos prácticos, la primera que debe estar preocupado es Cambiar sus contraseñas! No está claro a partir de su pregunta si su repositorio de git es completamente local o si aún tiene un repositorio remoto en otro lugar; si es remoto y no está protegido de los demás, tienes un problema. Si alguien ha clonado ese repositorio antes de arreglarlo, tendrá una copia de sus contraseñas en su máquina local, y no hay forma de que pueda obligarlos a actualizar a su versión "fija" sin que haya pasado del historial. Lo único seguro que puede hacer es cambiar su contraseña por otra cosa dondequiera que la haya usado.


Con eso fuera del camino, aquí está cómo solucionarlo. GitHub answered exactly that question as an FAQ:

Nota para usuarios de Windows: usar comillas (") en lugar de sencillos en este comando

git filter-branch --index-filter \ 
'git update-index --remove filename' <introduction-revision-sha1>..HEAD 
git push --force --verbose --dry-run 
git push --force 

Tenga en cuenta que una vez que ha empujado este código a un repositorio remoto como GitHub y otros han clonado ese repositorio remoto, ahora se encuentra en una situación en la que está reescribiendo el historial. Cuando otros intenten desplegar sus últimos cambios después de esto, recibirán un mensaje que indica que los cambios no se pueden aplicar porque no es un avance rápido.

Para solucionar esto, deberán eliminar su repositorio existente y volver a clonarlo, o seguir las instrucciones en "RECUPERACIÓN DE UPSTREAM REBASE" en el git-rebase manpage.


En el futuro, si accidentalmente cometer algunos cambios con información sensible, pero se nota antes empujando a un repositorio remoto, existen algunas soluciones más sencillas. Si la última confirmación es la de añadir la información confidencial, puede simplemente eliminar la información sensible, a continuación, ejecuta:

git commit -a --amend 

que modificará la confirmación anterior con los nuevos cambios que ha realizado, incluyendo el traslado de archivos completos hecho con un git rm. Si los cambios son más atrás en la historia, pero todavía no empujado a un repositorio remoto, se puede hacer un rebase interactivo:

git rebase -i origin/master 

que se abre un editor con las confirmaciones que ha realizado desde el último ancestro común con el mando a distancia repositorio. Cambie "pick" por "edit" en cualquier línea que represente una confirmación con información confidencial, y guárdela y salga.Git le guiará a través de los cambios, y dejar en un lugar donde puede:

$EDITOR file-to-fix 
git commit -a --amend 
git rebase --continue 

Para cada cambio con información sensible. Eventualmente, terminará de nuevo en su sucursal, y puede presionar con seguridad los nuevos cambios.

+4

Perfecto amigo, esa es una gran respuesta. Me salvaste el día. – zzeroo

+13

Solo para agregar un bit: en Windows, debe usar comillas dobles (") en lugar de simples. – ripper234

+0

No funcionó para mí, obteniendo un error cuando intento por segunda vez –

15

Recomiendo this script por David Underhill, funcionó como un encanto para mí.

Se añade estos comandos en el filtro de la rama además de natacado a limpiar el desorden que deja tras de sí:

rm -rf .git/refs/original/ 
git reflog expire --all 
git gc --aggressive --prune 

guión completo (todo el crédito a David Underhill)

#!/bin/bash 
set -o errexit 

# Author: David Underhill 
# Script to permanently delete files/folders from your git repository. To use 
# it, cd to your repository's root and then run the script with a list of paths 
# you want to delete, e.g., git-delete-history path1 path2 

if [ $# -eq 0 ]; then 
    exit 0 
fi 

# make sure we're at the root of git repo 
if [ ! -d .git ]; then 
    echo "Error: must run this script from the root of a git repository" 
    exit 1 
fi 

# remove all paths passed as arguments from the history of the repo 
[email protected] 
git filter-branch --index-filter \ 
"git rm -rf --cached --ignore-unmatch $files" HEAD 

# remove the temporary history git-filter-branch 
# otherwise leaves behind for a long time 
rm -rf .git/refs/original/ && \ 
git reflog expire --all && \ 
git gc --aggressive --prune 

Los últimos dos comandos puede funcionar mejor si se ha cambiado a lo siguiente:

git reflog expire --expire=now --all && \ 
git gc --aggressive --prune=now 
+1

Tenga en cuenta que su uso de caducar y podar son incorrectos, si no especifica la fecha, se predetermina a todos los commits anteriores a 2 semanas para podar . Lo que quiere es todo lo que se compromete, haga lo siguiente: 'git gc --aggressive --prune = now' –

+0

@Adam Parkin Voy a dejar el código en la respuesta igual porque es del script en el sitio de David Underhill, usted Podría comentar allí y si lo cambia, cambiaría esta respuesta ya que realmente no sé tan bien. El comando de expirar antes de la ciruela no afecta a eso, ¿verdad? –

+0

Éste en realidad funciona mejor que la respuesta aceptada. –

64

Cambiar su pa sswords es una buena idea, pero para el proceso de eliminación de contraseñas del historial de su repositorio, recomiendo el BFG Repo-Cleaner, una alternativa más rápida y sencilla al git-filter-branch explícitamente diseñado para eliminar datos privados de repositorios Git.

Crear un archivo private.txt lista de las contraseñas, etc, que desea eliminar (una entrada por línea) y luego ejecutar este comando:

$ java -jar bfg.jar --replace-text private.txt my-repo.git 

Todos los archivos bajo un umbral de tamaño (1 MB por defecto) en se escaneará el historial de su repositorio, y cualquier cadena coincidente (que no esté en su última confirmación) se reemplazará por la cadena "*** REMOVED ***". A continuación, puede utilizar git gc para limpiar los datos muerta:

$ git gc --prune=now --aggressive 

El BFG es típicamente 10-50x más rápido que ejecutar git-filter-branch y las opciones se han simplificado y adaptado en torno a estos dos casos de uso comunes:

  • Eliminación de archivos loco grande
  • Extracción contraseñas, credenciales & otros datos privados

La revelación completa: Soy el autor de la BFG Repo-Cleaner.

+0

Esta es una opción, pero podría romper su aplicación cuando se usen las contraseñas, p. para configurar una conexión de base de datos. Preferiría la respuesta actualmente aceptada porque aún es posible mantener las contraseñas en su copia de trabajo e ignorar los archivos que las contienen con .gitignore. – Henridv

+0

@Henridv No estoy seguro de cómo la respuesta aceptada por natacado difiere en ese sentido de mi propia respuesta? Ambas respuestas abordan específicamente la oración clave de la pregunta: "¿Hay alguna manera de eliminar todos los rastros de un archivo en particular en su historial de Git?" - es decir, hablan sobre la reescritura de la historia de Git. El problema de/how/NixNinja/should/supply passwords para su aplicación no se menciona ni en su pregunta ni en ninguna de las respuestas actuales. Da la casualidad que el BFG aborda específicamente el problema de las consecuencias no deseadas, consulte http://rtyley.github.com/bfg-repo-cleaner/#protected-commits –

+0

Interesante. +1. –

1

Por lo tanto, se ve algo como esto:

git rm --cached /config/deploy.rb 
echo /config/deploy.rb >> .gitignore 

Eliminar caché para el archivo de seguimiento de Git y añadir el archivo a .gitignore lista

8

Para que quede claro: La respuesta aceptada es correcto. Pruébalo primero. Sin embargo, puede ser innecesariamente complejo para algunos casos de uso, especialmente si encuentra errores desagradables como "fatal: bad revision - run-empty", o realmente no le importa el historial de su repositorio.

Una alternativa sería:

  1. cd a la rama de base del proyecto
  2. eliminar el código/archivo sensible
  3. rm-rf .git/# elimina toda la información de git su código
  4. Vaya a github y elimine su repositorio
  5. Siga esta guía para enviar su código a un nuevo repositorio como lo haría normalmente: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

Esto, por supuesto, eliminará todas las ramas del historial de confirmaciones y los problemas tanto del repositorio github como del repositorio git local. Si esto es inaceptable, deberá usar un enfoque alternativo.

Llama a esto la opción nuclear.

7

Si ya ha empujado a GitHub, los datos se ve comprometida, incluso si se fuerza alejarlo un segundo más tarde porque:

Para probar esto, he creado un acuerdo de recompra: https://github.com/cirosantilli/test-dangling y hecho:

git init 
git remote add origin [email protected]:cirosantilli/test-dangling.git 

touch a 
git add . 
git commit -m 0 
git push 

touch b 
git add . 
git commit -m 1 
git push 

touch c 
git rm b 
git add . 
git commit --amend --no-edit 
git push -f 

Si eliminar el repositorio sin embargo, se compromete hacer desaparecer incluso de la API de inmediato y darle 404, p.ej https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824 Esto funciona incluso si recrea otro repositorio con el mismo nombre.

Así que mi curso de acción recomendado es:

  • cambiar sus credenciales

  • si eso no es suficiente (por ejemplo, fotos desnuda):

    • eliminar el repositorio
    • contacto de soporte
5

Aquí está mi solución en ventanas

git filter-branch --tree-filtro "rm -f 'filedir/nombre de archivo'" CABEZA

git push --force

asegúrese de que la ruta es correcta de lo contrario no funcionará

espero que ayude

4

Puede usar git forget-blob.

El uso es bastante simple git forget-blob file-to-forget. Puede obtener más información aquí

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

desaparecerá de todas las confirmaciones en el historial, reflog, etiquetas, etc.

me encuentro con el mismo problema de vez en cuando, y cada vez que Tengo que volver a este post y a otros, es por eso que automaticé el proceso.

Créditos a contribuyentes de desbordamiento de pila que me permitieron armar esto

2

he tenido que hacer esto un par de veces al día. Tenga en cuenta que esto solo funciona en 1 archivo a la vez.

  1. Obtenga una lista de todas las confirmaciones que modificaron un archivo.El que está en la parte inferior será la primera comprometerse:

    git log --pretty=oneline --branches -- pathToFile

  2. Para eliminar el archivo de historial de uso de la primera cometen sha1 y la ruta al archivo de la orden anterior, y llenarlos en este comando:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

3

uso filter-branch:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all 

git push origin *branch_name* -f 
Cuestiones relacionadas