limpiar el historial de files eliminados, manteniendo el historial de files renombrados

Me gustaría extraer algunos files en un nuevo repository, manteniendo su historial, incluido el cambio de nombre de los files .

La respuesta mejor y más cercana que pude encontrar fue new-repo-with-copy-history-of-only-currently-tracked-files , usando git filter-branch --index-filter . Mantiene con éxito el historial de files existentes, pero no conserva el historial de files renombrados.

(Otra respuesta que pude encontrar fue usar git filter-branch --subdirectory-filter . Pero tiene dos problemas: no parece funcionar para todo el repository (carpeta '.') Y no conserva el historial de files renombrados. )

(Sin embargo, otra respuesta fue el uso git subtree . Pero no mantiene la historia en absoluto).

Así que probablemente estoy buscando una forma de mejorar el git ls-files > keep-these.txt desde la respuesta más cercana para también listr todos los nombres de files anteriores . Tal vez un guion?

Git no almacena los cambios de nombre de file.

Cada commit almacena un tree completo, por ejemplo, quizás commit 1234567... tiene los files README y foo.txt y confirma fedcba9 … tiene los files readme.txt y foo . Si le pides a git que compare commit 1234567 para confirmar fedcba9 , y README es suficientemente similar 1 a readme.txt , git dirá que la forma de transformar uno se compromete con el otro es cambiar el nombre del file. (Si el commit es el padre del otro, git show del child commit mostrará el cambio de nombre, porque git show calcula este cambio en git show time).

Por otro lado, si el segundo file readme es demasiado diferente, pero README es lo suficientemente similar a foo , git dirá que la forma de cambiar 1234567 para lograr fedcba9 es renombrar README a foo .

La key es que git computa eso cuando pides la comparación , y no un momento antes. No hay nada entre las confirmaciones que dice "renombrar algunos files". Git simplemente compara los commits y decide si los files son lo suficientemente similares.

Para sus propósitos, lo que esto significa en última instancia es que para cada confirmación en su secuencia de commits-to-copy-o-partial-copy, tendrá que decidir qué nombres de ruta conservar y cuáles descartar. Cómo lograr eso depende principalmente de usted. El command git log tiene un --follow flag para activar una cantidad limitada de detección de cambio de nombre, ya que funciona hacia atrás desde el niño se compromete con sus padres, y git blame automáticamente intenta hacer lo mismo; puede usar estos (un nombre de ruta a la vez) para get una asignación del formulario:

  in: commits A..B C..D E..F use path: dir/file.ext dir/frill.txt lib/frill.next 

por ejemplo. Pero no hay nada integrado para hacer esto, y no será particularmente fácil. Comenzaría por combinar git log --follow with --raw o --raw --name-status output y ver si hay algún Renames interesante detectado. Si y cuando los hay, esos son los límites de compromiso en los que querrá cambiar qué routes está guardando y descartando mientras trabaja a través de confirmaciones (ya sea con filter-branch o algún otro método).

Si eso no funciona, o si necesita más control, considere ejecutar git diff --name-status entre varios pares de commit (con información de par de commit proveniente de git rev-list ).


1 Siempre que haya solicitado la detección de cambio de nombre, "exactamente lo mismo" es suficientemente similar, como lo es hasta "50% similar". Puede ajustar la similitud requerida con el valor opcional que proporciona a la bandera -M git diff .


Editar : esto parece funcionar bien. Lo usé en el propio builtin/var.c , que solía tener dos nombres anteriores de acuerdo con esto:

 $ git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c 81b50f3ce40bfdd66e5d967bf82be001039a9a98 :100644 100644 2280518... 2280518... R100 builtin-var.c builtin/var.c 55b6745d633b9501576eb02183da0b0fb1cee964 :100644 100644 d9892f8... 2280518... R096 var.c builtin-var.c 

El --diff-filter suprime todo, pero cambia el nombre de las salidas para que podamos ver qué confirmación parece cambiar el nombre del file. Convertir esto en algo más útil requiere un poco más de trabajo, pero esto podría llevarte bastante lejos:

 git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c | while true; do if ! read hash; then break; fi IFS=$'\t' read mode_etc oldname newname read blankline echo in $hash, rename $oldname to $newname done 

que produjo:

 in 81b50f3ce40bfdd66e5d967bf82be001039a9a98, rename builtin-var.c to builtin/var.c in 55b6745d633b9501576eb02183da0b0fb1cee964, rename var.c to builtin-var.c