Cómo mover algunos archivos de un repositorio de git a otro (no un clon), preservando el historial

Resuelto ebneter asked hace 15 años • 16 respuestas

Nuestros repositorios Git comenzaron como partes de un único repositorio SVN monstruoso donde cada proyecto individual tenía su propio árbol, así:

project1/branches
        /tags
        /trunk
project2/branches
        /tags
        /trunk

Obviamente, fue bastante fácil mover archivos de uno a otro con svn mv. Pero en Git, cada proyecto está en su propio repositorio y hoy me pidieron que moviera un subdirectorio project2de 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. ¿Existe 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 a partir de parte de otro ( como en una pregunta anterior ).

ebneter avatar Sep 02 '09 09:09 ebneter
Aceptado

Si su historial es correcto, puede eliminar las confirmaciones como parche y aplicarlas en el nuevo repositorio:

cd repository
git log \
  --pretty=email \
  --patch-with-stat \
  --reverse \
  --full-index \
  --binary \
  -m \
  --first-parent \
  -- path/to/file_or_folder \
  > patch
cd ../another_repository
git am --committer-date-is-author-date < ../repository/patch 

O en una línea

git log --pretty=email --patch-with-stat --reverse --full-index --binary -m --first-parent -- path/to/file_or_folder | (cd /path/to/new_repository && git am --committer-date-is-author-date)

Sugerencia: si las confirmaciones en el subdirectorio del proyecto fuente deben extraerse a un nuevo directorio raíz del repositorio, git amse le puede dar un argumento como -p2eliminar directorios adicionales del parche.

(Tomado de los documentos de Exherbo )

Smar avatar Jul 11 '2012 05:07 Smar

Después de haber probado varios enfoques para mover un archivo o carpeta de un repositorio Git a otro, el único que parece funcionar de manera confiable se describe a continuación.

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 a este repositorio de destino.

La etapa uno

  1. Haga una copia del repositorio A ya que los siguientes pasos realizan cambios importantes en esta copia que no debe presionar.

    git clone --branch <branch> --origin origin --progress \
      -v <git repository A url>
    # eg. git clone --branch master --origin origin --progress \
    #   -v https://username@giturl/scm/projects/myprojects.git
    # (assuming myprojects is the repository you want to copy from)
    
  2. cd en él

    cd <git repository A directory>
    #  eg. cd /c/Working/GIT/myprojects
    
  3. Elimine el enlace al repositorio original para evitar realizar cambios remotos accidentalmente (por ejemplo, presionando)

    git remote rm origin
    
  4. 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.

    git filter-branch --subdirectory-filter <directory> -- --all
    # eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. Para mover solo un archivo: revise lo que queda y elimine todo excepto el archivo deseado. (Es posible que deba eliminar los archivos que no desea con el mismo nombre y confirmar).

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    # eg. FILE_TO_KEEP = pom.xml to keep only the pom.xml file from FOLDER_TO_KEEP
    

Etapa dos

  1. Paso de limpieza

    git reset --hard
    
  2. Paso de limpieza

    git gc --aggressive
    
  3. Paso de limpieza

    git prune
    

Es posible que desee importar estos archivos al repositorio B dentro de un directorio que no sea la raíz:

  1. Haz ese directorio

    mkdir <base directory>             eg. mkdir FOLDER_TO_KEEP
    
  2. Mover archivos a ese directorio

    git mv * <base directory>          eg. git mv * FOLDER_TO_KEEP
    
  3. Agregar archivos a ese directorio

    git add .
    
  4. Confirme sus cambios y estaremos listos para fusionar estos archivos en el nuevo repositorio.

    git commit
    

Etapa tres

  1. Haga una copia del repositorio B si aún no tiene una

    git clone <git repository B url>
    # eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (asumiendo que FOLDER_TO_KEEP es el nombre del nuevo repositorio al que está copiando)

  2. cd en él

    cd <git repository B directory>
    #  eg. cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. Cree una conexión remota al repositorio A como una sucursal en el repositorio B

    git remote add repo-A-branch <git repository A directory>
    # (repo-A-branch can be anything - it's just an arbitrary name)
    
    # eg. git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. Extraiga de esta rama (que contiene solo el directorio que desea mover) al repositorio B.

    git pull repo-A-branch master --allow-unrelated-histories
    

    La extracción copia tanto los archivos como el historial. Nota: Puede utilizar una combinación en lugar de una extracción, pero la extracción funciona mejor.

  5. Finalmente, probablemente quieras limpiar un poco eliminando la conexión remota al repositorio A.

    git remote rm repo-A-branch
    
  6. Empuja y ya está todo listo.

    git push
    
mcarans avatar Jul 11 '2014 09:07 mcarans

Sí, presionar el --subdirectory-filterbotón de filter-branchfue 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 hashes. Dado que ninguno de los comandos estándar (por ejemplo pull, ) reescribe el historial, no hay forma de usarlos para lograrlo.

Por supuesto, podría refinar los detalles (parte de la 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 de git no es facilitar la reescritura de la historia.

Cascabel avatar Sep 02 '2009 05:09 Cascabel

Esto se vuelve más sencillo usando git-filter-repo.

Para desplazarse project2/sub/dira project1/sub/dir:

# Create a new repo containing only the subdirectory:
git clone project2 project2_clone --no-local
cd project2_clone
git filter-repo --path sub/dir

# Merge the new repo:
cd ../project1
git remote add tmp ../project2_clone/
git fetch tmp master
git merge remotes/tmp/master --allow-unrelated-histories
git remote remove tmp

Para instalar la herramienta simplemente: pip3 install git-filter-repo ( más detalles y opciones en README )

# Before: (root)
.
|-- project1
|   `-- 3
`-- project2
    |-- 1
    `-- sub
        `-- dir
            `-- 2

# After: (project1)
.
├── 3
└── sub
    └── dir
        └── 2
Tapuzi avatar May 20 '2020 16:05 Tapuzi