Preserve –no-ff merge se compromete con git filter-branch –subdirectory-filter

Tengo un repository git con la siguiente estructura:

.git/ project-root/ abc/ xyz/ 

Y esto es lo que quiero:

 project-root/ .git abc/ xyz/ 

He estado investigando cómo lograr esto y esto es lo que he encontrado hasta ahora: http://git-scm.com/docs/git-filter-branch

De acuerdo con eso, debería lograr lo que quiero con el siguiente command:

 git filter-branch --subdirectory-filter project-root -- --all 

Esto reescribe el historial de compromisos, pero la mayoría de las asignaciones de fusión no avanza se pierden (ver image a continuación).

¿Hay alguna forma de preservar esos compromisos?

enter image description here

Intenté usar –tree-filter en lugar de –subdirectory-filter y parece funcionar para mí. Este es el command que he usado:

 git filter-branch --tree-filter 'git mv -k project-root/{,.[!.],..?}* .' -- --all 

Después de ejecutar este command, el historial de confirmaciones se reescribe como si "git-project" nunca hubiera sido rastreado, y la topología de las twigs se conserva, exactamente como yo quería.

Si alguien intenta usar esto en Windows y obtiene un error de "permiso denegado", matar el process explorer.exe podría ayudar (encontré esta solución aquí: http://academe.co.uk/2011/12/git-mv- permission-denied / ).

Editar: El command anterior no reescribe las tags. Para reescribir las tags, el command correcto es:

git filter-branch –tree-filter 'git mv -k proyecto-root /{,.[!.],..?}*.' –tag-name-filter cat – –todo

Como los documentos señalan aquí :

 --subdirectory-filter <directory> Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root. Implies [Remap_to_ancestor]. 

Creo que el problema es que la fusión se compromete en realidad no toca el subdirectory dado, lo que significa que el subdirectory-filter ni siquiera los mira, y por lo tanto no puede conservarlos. Por lo tanto, debemos usar otro filter que examine cada compromiso. tree-filter es perfecto para esto, pero debemos escribir un script para hacer lo que queremos que se haga en cada commit.

Además, mi problema era en realidad un poco más amplio que el ejemplo, ya que mi carpeta project-root tenía hermanos que quería eliminar. El filter de subdirectory los eliminaría, pero para hacerlo con el filter de tree fue útil find para ayudarlo.

Era conveniente poner los commands para que se ejecuten cada vez en un file separado.

para un repository estructurado de esta manera:

 .git/ someDirectory/ someFile.txt otherDirectory/ dontDeleteThisOne/ project-root/ xyz/ abc/ 

Esto es lo que funcionó para mí:

 git filter-branch --tree-filter /absolute/path/to/filterScript.sh --tag-name-filter cat -- --all 

donde /absolute/path/to/filterScript.sh es un script ejecutable que contiene esto:

 #!/bin/bash #save the folder dontDeleteThisOne so it won't get deleted git mv -fk dontDeleteThisOne project-root && \ #remove everything not under project-root find . -maxdepth 1 -mindepth 1 '(' -name project-root -o -name '.git' ')' -prune -o -exec git rm -rf --ignore-unmatch '{}' ';' && \ #move the contents of the project-root directory up to root git mv -fk project-root/{,.[!.],..?}* .; 

la estructura repo resultante es entonces esto:

 .git/ dontDeleteThisOne/ xyz/ abc/ 

Este resultado es equivalente al resultado de git filter-branch --subdirectory-filter project-root , EXCEPTO que las git filter-branch --subdirectory-filter project-root fusión se conservan en el historial, como se desee.

Por supuesto, esto lleva MUCHO más time que usar el filter de subdirectory …