Nuestros repositorios Git comenzaron como partes de un único repositorio SVN monstruoso donde los proyectos individuales tenían cada uno su propio árbol de la siguiente manera:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
Obviamente, era bastante fácil mover archivos de uno a otro svn mv
. Pero en Git, cada proyecto está en su propio repositorio, y hoy me pidieron que moviera un subdirectorio de project2
a project1
. Hice algo como esto:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push
Pero eso parece bastante complicado. ¿Hay una mejor manera de hacer este tipo de cosas en general? ¿O he adoptado el enfoque correcto?
Tenga en cuenta que esto implica fusionar el historial en un repositorio existente, en lugar de simplemente crear un nuevo repositorio independiente de parte de otro ( como en una pregunta anterior ).
git
repository
ebneter
fuente
fuente
git fetch p2 && git merge p2
lugar degit fetch p2 && git branch .. && git merge p2
? Editar: bien, parece que desea obtener los cambios en una nueva rama llamada p2, no en la rama actual.--allow-unrelated-histories
al últimogit merge
para que funcione.Respuestas:
Sí, golpear el
--subdirectory-filter
defilter-branch
era la clave. El hecho de que lo haya usado esencialmente demuestra que no hay una manera más fácil: no tuvo más remedio que reescribir el historial, ya que quería terminar con solo un subconjunto (renombrado) de los archivos, y esto, por definición, cambia los hash. Dado que ninguno de los comandos estándar (ppull
. Ej. ) Reescribe el historial, no hay forma de que pueda usarlos para lograr esto.Podría refinar los detalles, por supuesto, parte de su clonación y ramificación no era estrictamente necesaria, ¡pero el enfoque general es bueno! Es una pena que sea complicado, pero, por supuesto, el objetivo no es facilitar la reescritura de la historia.
fuente
--index-filter
la página defilter-branch
manual.Si su historial es correcto, puede tomar los commits como parche y aplicarlos en el nuevo repositorio:
O en una linea
(Tomado de los documentos de Exherbo )
fuente
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Funciona sin problemas AFAICT.--committer-date-is-author-date
opción para preservar la fecha de confirmación original en lugar de la fecha en que se movieron los archivos.git log
, por lo que no funciona con ambos--follow
y--reverse
correctamente). Utilicé esta respuesta , y aquí hay un script completo que uso ahora para mover archivosDespués de probar varios enfoques para mover un archivo o carpeta de un repositorio de Git a otro, a continuación se describe el único que parece funcionar de manera confiable.
Implica clonar el repositorio desde el que desea mover el archivo o carpeta, mover ese archivo o carpeta a la raíz, reescribir el historial de Git, clonar el repositorio de destino y extraer el archivo o carpeta con el historial directamente en este repositorio de destino.
La etapa uno
¡Haga una copia del repositorio A ya que los siguientes pasos hacen cambios importantes a esta copia que no debe presionar!
cd en ella
Elimine el enlace al repositorio original para evitar realizar cambios remotos accidentalmente (por ejemplo, presionando)
Revise su historial y archivos, eliminando todo lo que no esté en el directorio 1. El resultado es el contenido del directorio 1 arrojado a la base del repositorio A.
Solo para mover un solo archivo: revise lo que queda y elimine todo excepto el archivo deseado. (Es posible que deba eliminar archivos que no desea con el mismo nombre y confirmación).
Etapa dos
Paso de limpieza
Paso de limpieza
Paso de limpieza
Es posible que desee importar estos archivos en el repositorio B dentro de un directorio, no la raíz:
Haz ese directorio
Mover archivos a ese directorio
Agregar archivos a ese directorio
Confirme sus cambios y estamos listos para fusionar estos archivos en el nuevo repositorio
Etapa tres
Haga una copia del repositorio B si aún no tiene uno
(suponiendo que FOLDER_TO_KEEP es el nombre del nuevo repositorio al que está copiando)
cd en ella
Cree una conexión remota al repositorio A como una rama en el repositorio B
Tire de esta rama (que contiene solo el directorio que desea mover) al repositorio B.
La extracción copia tanto los archivos como el historial. Nota: Puede usar una combinación en lugar de un pull, pero pull funciona mejor.
Finalmente, probablemente desee limpiar un poco eliminando la conexión remota al repositorio A
Empuja y estás listo.
fuente
He encontrado esto muy útil. Es un enfoque muy simple en el que crea parches que se aplican al nuevo repositorio. Vea la página vinculada para más detalles.
Solo contiene tres pasos (copiados del blog):
El único problema que tuve fue que no podía aplicar todos los parches a la vez usando
En Windows recibí un error de argumento no válido. Así que tuve que aplicar todos los parches uno tras otro.
fuente
MANTENER EL NOMBRE DEL DIRECTORIO
El subdirectorio-filtro (o el comando más corto git subtree) funciona bien pero no funcionó para mí ya que eliminan el nombre del directorio de la información de confirmación. En mi caso, solo quiero fusionar partes de un repositorio en otro y conservar el historial CON el nombre completo de la ruta.
Mi solución fue usar el filtro de árbol y simplemente eliminar los archivos y directorios no deseados de un clon temporal del repositorio de origen, luego extraer de ese clon al repositorio de destino en 5 simples pasos.
fuente
El que siempre uso está aquí http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Simple y rapido.
Para cumplir con los estándares de stackoverflow, este es el procedimiento:
fuente
Esta respuesta proporciona comandos interesantes basados
git am
y presentados usando ejemplos, paso a paso.Objetivo
Procedimiento
git log --pretty=email -p --reverse --full-index --binary
git am
1. Extraiga el historial en formato de correo electrónico
Ejemplo: historia Extracto de
file3
,file4
yfile5
Limpie el destino del directorio temporal
Limpia tu fuente de repositorio
Extraiga el historial de cada archivo en formato de correo electrónico
Lamentablemente opción
--follow
o--find-copies-harder
no se puede combinar con--reverse
. Es por eso que el historial se corta cuando se cambia el nombre del archivo (o cuando se cambia el nombre de un directorio principal).Después: historial temporal en formato de correo electrónico
2. Reorganice el árbol de archivos y actualice el cambio de nombre de archivo en el historial [opcional]
Suponga que desea mover estos tres archivos en este otro repositorio (puede ser el mismo repositorio).
Por lo tanto, reorganice sus archivos:
Tu historial temporal es ahora:
Cambie también los nombres de archivo dentro del historial:
Nota: Esto reescribe el historial para reflejar el cambio de ruta y nombre de archivo.
(es decir, el cambio de la nueva ubicación / nombre dentro del nuevo repositorio)
3. Aplicar nuevo historial
Su otro repositorio es:
Aplicar confirmaciones de archivos de historial temporales:
Su otro repositorio es ahora:
Use
git status
para ver la cantidad de confirmaciones listas para ser empujadas :-)Nota: Como el historial se ha reescrito para reflejar la ruta y el cambio de nombre de archivo:
(es decir, en comparación con la ubicación / nombre dentro del repositorio anterior)
git mv
cambiar la ubicación / nombre de archivo.git log --follow
acceder al historial completo.Truco adicional: detecte archivos renombrados / movidos dentro de su repositorio
Para enumerar los archivos que han cambiado de nombre:
Más personalizaciones: puede completar el comando
git log
con las opciones--find-copies-harder
o--reverse
. También puede eliminar las dos primeras columnas utilizandocut -f3-
y agrupando el patrón completo '{. * =>. *}'.fuente
Después de haber tenido un picor similar desde cero (aunque solo para algunos archivos de un repositorio dado), este script demostró ser realmente útil: git-import
La versión corta es que crea archivos de parche del archivo o directorio dado (
$object
) desde el repositorio existente:que luego se aplican a un nuevo repositorio:
Para más detalles, consulte:
fuente
Prueba esto
cd repo1
Esto eliminará todos los directorios excepto los mencionados, conservando el historial solo para estos directorios
Ahora puede agregar su nuevo repositorio en su control remoto git y empujarlo a eso
agregar
-f
para sobrescribirfuente
Usando la inspiración de http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , creé esta función Powershell para hacer lo mismo, que tiene funcionó muy bien para mí hasta ahora:
Uso para este ejemplo:
Después de hacer esto, puede reorganizar los archivos en la
migrate-from-project2
rama antes de fusionarlos.fuente
Quería algo robusto y reutilizable (función one-command-and-go + undo), así que escribí el siguiente script bash. Trabajó para mí en varias ocasiones, así que pensé en compartirlo aquí.
Es capaz de mover una carpeta arbitraria
/path/to/foo
derepo1
dentro/some/other/folder/bar
arepo2
(las rutas de la carpeta pueden ser iguales o diferentes, la distancia desde la carpeta raíz puede ser diferente).Dado que solo pasa por las confirmaciones que tocan los archivos en la carpeta de entrada (no sobre todas las confirmaciones del repositorio de origen), debería ser bastante rápido incluso en repositorios de gran fuente, si solo extrae una subcarpeta profundamente anidada que no se tocó en cada cometer.
Como lo que esto hace es crear una rama huérfana con toda la historia del repositorio anterior y luego fusionarla con el HEAD, incluso funcionará en caso de conflictos de nombres de archivo (entonces tendría que resolver una fusión al final del curso) .
Si no hay conflictos de nombre de archivo, solo necesita al
git commit
final para finalizar la fusión.La desventaja es que probablemente no seguirá los cambios de nombre de los archivos (fuera de la
REWRITE_FROM
carpeta) en las solicitudes de repositorio de origen bienvenidas en GitHub para acomodar eso.Enlace de GitHub: git-move-folder-between-repos-keep-history
fuente
En mi caso, no necesitaba preservar el repositorio del que estaba migrando ni preservar ningún historial anterior. Tenía un parche de la misma rama, desde un control remoto diferente
En esos dos pasos, pude hacer que la rama del otro repositorio apareciera en el mismo repositorio.
Finalmente, configuré esta rama (que importé del otro repositorio) para seguir la línea principal del repositorio objetivo (para poder diferenciarlos con precisión)
Ahora se comportó como si fuera solo otra rama que había empujado contra ese mismo repositorio.
fuente
Si las rutas para los archivos en cuestión son las mismas en los dos repositorios y desea traer solo un archivo o un pequeño conjunto de archivos relacionados, una forma fácil de hacerlo es usarlo
git cherry-pick
.El primer paso es llevar los commits del otro repositorio a su propio repositorio local usando
git fetch <remote-url>
. Esto dejará deFETCH_HEAD
apuntar a la confirmación del jefe del otro repositorio; si desea conservar una referencia a esa confirmación después de haber realizado otras recuperaciones, puede etiquetarlagit tag other-head FETCH_HEAD
.Luego, deberá crear una confirmación inicial para ese archivo (si no existe) o una confirmación para llevar el archivo a un estado que pueda ser parcheado con la primera confirmación del otro repositorio que desea incorporar. Puede ser capaz de hacer esto con un archivo introducido
git cherry-pick <commit-0>
sicommit-0
lo desea, o puede que necesite construir el commit 'a mano'. Agregue-n
a las opciones de selección de cereza si necesita modificar la confirmación inicial para, por ejemplo, descartar archivos de esa confirmación que no desea incorporar.Después de eso, puede continuar con
git cherry-pick
las confirmaciones posteriores, nuevamente utilizando-n
cuando sea necesario. En el caso más simple (todos los envíos son exactamente lo que quiere y aplicar limpiamente) se puede dar la lista completa de confirmaciones en la línea de comandos de cereza-Pick:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.fuente
Esto se vuelve más simple usando git-filter-repo.
Para pasar
project2/sub/dir
aproject1/sub/dir
:Para instalar la herramienta simplemente:
pip3 install git-filter-repo
( más detalles y opciones en README )fuente
El siguiente método para migrar mi GIT Stash a GitLab manteniendo todas las ramas y preservando el historial.
Clonar el antiguo repositorio a local.
Crea un repositorio vacío en GitLab.
Lo anterior lo realicé cuando migramos nuestro código de alijo a GitLab y funcionó muy bien.
fuente