¿Cómo deshabilitar "remap to ancestro" para las tags en git-filter-branch?

Mi problema es muy similar a esa pregunta , y esa respuesta funciona perfectamente para mí.

El único problema es con las tags: recibo muchas tags innecesarias en el repository resultante.

Este es mi command:

git filter-branch --tag-name-filter cat --prune-empty --index-filter "git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- path/to/dir1 path/to/dir2" -- --all 

Con opción -- --all las tags se conservan; las tags que señalan las confirmaciones omitidas se mueven al compromiso de ancestro más cercano.
Sin opción -- --all todas las tags se pierden (si no están enlistdas explícitamente en la línea de command, por supuesto).

Quiero que las tags que señalan las confirmaciones omitidas se excluyan automáticamente en lugar de moverlas a la confirmación ancestro más cercana.
Todas las demás tags deben conservarse.
¿Cómo puedo hacer esto?

PD
Me gustaría evitar eliminar tags innecesarias manualmente antes de ejecutar git filter-branch .
Hay miles de tags en el repository.


Actualizar:
Gracias a @torek por la confirmación de que no hay una manera directa.
Resolví mi problema ejecutando el script Lua, que borra todas las tags innecesarias.

 local list_of_dirs = "path/to/dir1 path/to/dir2" -- separated by space local useful = {} for line in io.popen( "git log --full-history --decorate=full --format=%D -- "..list_of_dirs ):lines() do for tag in line:gmatch"tag: refs/tags/([^,]+)" do -- Limitation: your tag names should not contain comma useful[tag] = true end end for tag in io.popen"git tag":lines() do if not useful[tag] then os.execute('git tag -d "'..tag..'"') end end 

Lamentablemente, no hay forma de pedirle a git filter-branch que lo haga automáticamente. Vea a continuación (muy abajo :-)) una idea para modificar el código para que sea posible (esto puede ser más fácil y más confiable que la siguiente sección).

Afortunadamente, hay una manera de automatizar el descubrimiento de tags reasignadas a sus confirmaciones originales, vs tags reasignadas a alguna otra confirmación (por lo tanto ancestro). Lamentablemente, nunca he hecho esto, así que lo siguiente básicamente es solo teoría, en lugar de práctica.


El primer paso será build tu propio map. Querrá, para cada label, identificar el object labeldo final:

 git for-each-ref --format '%(refname)' refs/tags | while read name; do echo $name $(git rev-parse $name^{}); done 

(Este método de dos pasos, en lugar de usar %(object) , parece ser necesario para asignar tags al object final en caso de que una label apunte a otra label, si no lo tiene). El resultado de lo anterior es un map de nombre a object. Necesitará un map que corresponda al estado "anterior", por lo tanto, ejecútelo antes de filtrar (o en una copy "no filtrada", ver a continuación).

Es posible que desee limitarse a las tags que, en última instancia, apuntan a confirmaciones (consulte la alternativa siguiente sobre la modificación filter-branch ).

Una vez que haya terminado su filter-branch , use el mismo command para get un nuevo map. (Redirige las salidas de ambos commands a un file temporal).

Si lo prefiere, puede hacerlo solo una vez, después de filtrar, suministrando un filter de nombre de label que asigna nombres de tags antiguas a nuevos nombres de label únicos y distinguibles. Por ejemplo, si todas sus tags existentes se ajustan al v number . number patrón v number . number v number . number puede hacer que su filter de tags produzca tags comenzando con w lugar. Eso hace que sea fácil decir, en el repository posterior al filtrado, qué label fue cuál. Finalmente, tendrá que cambiar el nombre de todas las tags, por supuesto.

O bien, dado que debe filtrar una copy del repository original, puede ejecutar el for-each-ref en el repository original para el mapeo "antiguo" y nuevamente en el repository filtrado para el mapeo "nuevo". O bien, compruebe en el refs/original/refs/tags/ name-space para encontrar las tags originales (no estoy seguro de si filter-branch guarda las tags originales como esta, de la forma en que guarda los nombres de las twigs originales refs).

La tarea restante es la parte difícil: ahora debemos averiguar si el nuevo object objective "es" el object objective original (después del filtrado), o si se encuentra algún ancestro a través de la reasignación de antecesor. Aquí es donde nos volvemos teóricos, porque lo que hace su (s) filter (s) de filter de filter es importante. ¿Cómo podemos decir si commit 89abcde "es" el resultado filtrado de "1234567", o si simplemente saltamos esa confirmación? Esto, por supuesto, depende de cuáles sean tus filters .

Debido a que filter-branch deja todos los commits originales en el repository junto con sus copys, con los nombres de las twigs originales almacenados en refs/original/refs/... , podemos ver todas las confirmaciones originales. Esto significa que podemos ejecutar los dos maps y comparar los commits, o volver a ejecutar los filters, para hacer este tipo de descubrimiento.

Si sus filters siempre dejan el tree intacto, es posible que podamos usar git cat-file -p <commit-id> | headergrep tree git cat-file -p <commit-id> | headergrep tree para extraer las identificaciones de tree. Si los identificadores de tree de la confirmación antigua y nueva coinciden, conservamos esa confirmación, por lo que deseamos conservar la label; si no, deseamos descartar la label. (Tenga en count que debe escribir headergrep : es simplemente un grep de los contenidos hasta la primera línea en blanco, que separa los encabezados de confirmación del post de confirmación).

Si sus filters siempre dejan todo excepto el tree intacto, es posible que podamos extraer todo, excepto el tree y parent líneas parent . Esto es iffier desde una confirmación anterior que dice:

 tree ... parent ... author AU Thor <thor@example.com> 1471018671 -0700 committer AU Thor <thor@example.com> 1471018671 -0700 terriblecommitmessage 

puede parecer lo mismo que una confirmación nueva, pero reasignada, que usa exactamente el mismo post, y es del mismo autor y confirmador y se realiza en el mismo segundo para que las marcas de time coincidan (esto podría suceder si se realizan confirmaciones de forma automática) software que realiza múltiples confirmaciones por segundo). En general, sin embargo, el contenido de una confirmación copyda coincidirá (después de descartar el tree y las líneas parentales), mientras que el contenido de una confirmación reasignada no lo hará. Por lo tanto podemos hacer hash y comparar text, o comparar text sin formatting, usando el equivalente de headergrep -v (que debe escribir nuevamente: es una variante directa de nuestro headergrep teórico anterior, excepto que con -v debemos copyr la línea en blanco y confirmar post, así como todas excepto las líneas de encabezado excluidas) con salida enviada a files temporales y cmp , o con salida enviada a través de git hash-object : podemos simular que estas líneas de salida de headergrep -v son blobs y obtienen su SHA-1 único hash IDs, y compararlos.

Por supuesto, si su filter hace algo muy fácilmente identificable, como omitir commits con algún autor en particular (como en uno de los ejemplos de documentation), será fácil saber qué commits se omitieron y por lo tanto causó un remap-ancester.

Una vez que sabemos qué confirmaciones se conservaron y cuáles se reasignaron, sabemos qué tags conservar (conservar) o descartar (reasignar). Ahora solo se trata de eliminar todas las tags de "descarte".


Otra posibilidad sería copyr el script de filter-branch :

 $ less $(git --exec-path)/git-filter-branch #!/bin/sh # # Rewrite revision history # Copyright (c) Petr Baudis, 2006 ... [snip] 

Tenga en count que el filter de nombre de label se ejecuta después de que el código remap_to_ancestor maneja los nombres de las twigs que apuntan a commits que fueron descartados, y por lo tanto reasignado (creando "$workdir"/../map/$sha1 ). Si lo mueves para ejecutar antes de ese punto, puedes saber fácilmente qué commits se saltaron. De hecho, el código para reasignar esa label no hace nada si la confirmación del objective de la label no está en el map o si el objective de la label no es una confirmación. (En este caso, querrá eliminarlo. No está del todo claro qué es lo que desea hacer con las confirmaciones que apuntan a treees o blobs).