2009-07-23 10 views
13

Tengo los controles remotos Foo y Bar. Foo es una aplicación web que tiene muchos directorios, entre ellos el /public que contiene una variedad de archivos y otros directorios.git: ¿puedo subtree fusionar solo un subtempo de un repositorio?

Bar es un conjunto de bibliotecas y lo que no se usa en la interfaz, como tal, debería ir en /public/bar en Foo. Foo no tiene archivos allí.

Eso sería pan comido con cualquier submódulo o combinación de subárbol. Sin embargo ...

El árbol de Bar es desordenado, tiene todo tipo de archivos de preproducción como PSD y FLA, y la única parte realmente útil es lo que hay dentro de su /www/tools.

Entonces, lo que quiero hacer es unir el /www/tools de Bar en Foo's /public/bar, y pretender que el resto del árbol de Bar ni siquiera existe.

¿Se puede hacer?

(yo supongo que esto es muy similar a cómo combinar de un proyecto que originalmente se fusionó el suyo como un sub-árbol. Lo que no sé cómo hacerlo, tampoco.)

Respuesta

0

no creo que esto se puede hacer mediante una combinación, per-se, pero estos pasos pueden ser el truco, al menos en lo que se refiere al producto final. Primero:

% git fetch Bar 

Esto recupera los últimos commit (s) del repositorio de barras, pero no intenta fusionarlos. Se registra el SHA para el comprometerse en .git/FETCH_HEAD

% cat .git/FETCH_HEAD 
b91040363160aab4b5dd46e61e42092db74b65b7    branch 'Bar' of ssh://blah... 

Esto muestra lo que el identificador de SHA para la última confirmación desde la rama remota es.

% git checkout b91040363160aab4b5dd46e61e42092db74b65b7 www/tools 

esto toma la copia de los archivos en www/herramientas para la descabellada comprometen y sobrescribe lo que hay en el árbol de trabajo. A continuación, puede enviar esos cambios a su repositorio local como es normal. La confirmación resultante no tendrá ninguna referencia de dónde vino, pero al menos debería obtener su repositorio con las versiones de los archivos que desea.

+0

Sí, estaría más feliz manteniendo la historia. Tal vez se podría hacer algo con filter-branch. – kch

+1

1.) puede usar "git checkout FETCH_HEAD www/tools" 2.) probablemente pueda usar "git checkout --merge FETCH_HEAD www/tools" 3.) después de registrar el estado requerido en el directorio de trabajo, puede crear merge a mano escribiendo FETCH_HEAD en .git/MERGE_HEAD antes de git-commit –

+0

Ah, sabía que tenía que haber una forma mejor de hacer la parte FETCH_HEAD –

2

Editar: Se me ocurre que puede hacer esto solo con git merge --no-commit. Esto intentará la fusión, e incluso si no entra en conflicto, se detendrá justo antes de comprometerse. En este punto, puedes eliminar toda la basura que no necesites (incluida la restauración de archivos en conflicto si es necesario) y crear una combinación de fusión que solo contenga el subárbol deseado.

Respuesta original:

Usted puede utilizar efectivamente filter-branch para esto. Un esquema:

clonar tu repositorio fuente:

git clone --bare /path/to/bar /path/to/bar_clone 

Utilizando un clon desnuda le ahorrará el tiempo y el espacio de la creación de un directorio de trabajo.

A continuación, utilice filtro de rama en el clon:

git filter-branch --index-filter 'git rm -rf <unwanted files/directories>' -- --all 

El --all lo deja saber que usted desea utilizar todos los árbitros, no sólo la cabeza actual.Ahora tendrá un repositorio que contiene solo el subdirectorio deseado y todo el historial asociado con él.

Nota: Lo siento, no conozco una forma muy sencilla de eliminar todo, excepto lo que desee. Tienes que tener cuidado con los comodines porque no quieres pegarle a ningún directorio de git. Esto es algo que va a trabajar, aunque es más lento, especialmente si usted tiene una gran cantidad de archivos:

git filter-branch --index-filter 'git rm -f `git ls-files | grep -v ^www/tools`' -- --all 

De todos modos, sin embargo a gestionar la lista de archivos para eliminar, puede seguir adelante con su combinación de sub-árbol, tirando desde bar_clone al foo.

3

Intente utilizar git subtree para esto. Le permite extraer un subárbol de un proyecto en otro proyecto, que luego puede combinar con el suyo.

+0

¿Puedes especificar cómo hacerlo? Estoy tratando de resolver este problema aquí: http://stackoverflow.com/questions/30704352/sub-directory-into-independent-repository-and-later-merge-back-into-main-reposit –

10

He hecho esto con éxito usando los siguientes comandos (reescrito para su escenario).

$ git remote add Bar /path/to/bar 
$ git merge -s ours --no-commit Bar/master 
$ git read-tree --prefix=public/bar -u Bar/master:www/tools/ 

HUGE ADVERTENCIA! Todavía estoy en el proceso de averiguar cómo hacer buscar/fusionar desde la barra. Esto traerá todo solo una vez.

mi actual forma de combinar cambios es así:

$ get fetch Bar 
$ git pull -X subtree=public/bar Bar master 

Esto le dejará con los conflictos que dicen que una tonelada de archivos se han eliminado, sólo puede git rm ellos y luego git commit.

Estoy ciertamente abierto a sugerencias sobre una mejor manera de lograr cambios.

Dado que este es un hilo viejo, debo agregar que estoy ejecutando Git 1.7.9.

+0

Un problema con esto es que trae todos los commits de la rama, no solo los que afectan al subdirectorio. ¿Hay alguna manera de evitar eso? – pmr

+0

Si está fusionando varios repositorios, es posible que desee pasar '--allow-unrelated-history' para fusionar. –

0

Sugeriría usar el subárbol dos veces - una para extraer todas las/www/tools y una vez para extraer/público - parece que/public debería estar en su repositorio así que, le sugiero que presione/publique a un nuevo repo - con todo su historial de subárboles, luego combine el historial del subárbol de/www/tools en el repositorio nuevo/público y vuelva a agregarlo como un subárbol de Foo.

cd foo 
git subtree split --prefix=public --branch=new-shared-public --annotate='(split) ' 
cd../bar 
git subtree split --prefix=www/tools --rejoin --branch=new-shared-www-tools --annotate='(split) ' 

Ahora tiene dos nuevas sucursales en sus repositorios. Uno con el historial de compromisos del público y el otro con www/tools. Voy a omitir cd a partir de ahora.

Crea tu nueva operación pública (github Asumo aquí, pero suena como es posible que desee hacerlo localmente) y empujar a su sub-árbol que hay: git checkout-nueva-compartida pública git [email protected] empuje: my_id/nueva-compartido-repo.git CABEZA: amo

luego fusionar su otra rama en la que:

git checkout new-shared-www-tools 
git remote add Foo [email protected]:my_id/new-shared-repo.git 
git merge -s ours --no-commit Bar/master 

Aunque he especificado ours como la política de comprometerse, es probable que (probablemente ) doesn no importa

Finalmente, después de que haya realizado la fusión y la haya enviado al nuevo repositorio, vuelva al repositorio de Foo.Administre la historia pública desde allí y agregue el nuevo repositorio público como un subárbol:

git subtree add --squash --prefix shared [email protected]:my_id/new-shared-repo.git master 
git subtree pull --squash --prefix shared [email protected]:my_id/new-shared-repo.git master 
git push 
Cuestiones relacionadas