tag - git remote
Fusiona dos repositorios Git sin romper el historial de archivos (5)
Necesito fusionar dos repositorios Git en un nuevo y tercer repositorio. He encontrado muchas descripciones de cómo hacerlo utilizando una combinación de subárbol (por ejemplo, la respuesta de Jakub NarÄ™bski sobre ¿Cómo se fusionan dos repositorios Git? ) Y seguir esas instrucciones en su mayoría funciona, excepto que cuando confirmo el subárbol fusionar todos los archivos desde los repositorios antiguos se registran como nuevos archivos agregados. Puedo ver el historial de confirmaciones de los repositorios antiguos cuando hago git log
, pero si hago git log <file>
, solo se muestra una confirmación para ese archivo: la fusión del subárbol. A juzgar por los comentarios sobre la respuesta anterior, no estoy solo al ver este problema, pero no he encontrado soluciones publicadas para ello.
¿Hay alguna manera de fusionar repositorios y dejar intacto el historial de archivos individuales?
Convertí la solution de @Flimm en un git alias
como este (agregado a mi ~/.gitconfig
):
[alias]
mergeRepo = "!mergeRepo() { /
[ $# -ne 3 ] && echo /"Three parameters required, <remote URI> <new branch> <new dir>/" && exit 1; /
git remote add newRepo $1; /
git fetch newRepo; /
git branch /"$2/" newRepo/master; /
git checkout /"$2/"; /
mkdir -vp /"${GIT_PREFIX}$3/"; /
git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} /"${GIT_PREFIX}$3/"/; /
git commit -m /"Moved files to ''${GIT_PREFIX}$3''/"; /
git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames /"$2/"; /
git branch -D /"$2/"; git remote remove newRepo; /
}; /
mergeRepo"
Esta es una forma que no reescribe ningún historial, por lo que todos los ID de confirmación seguirán siendo válidos. El resultado final es que los archivos del segundo repositorio terminarán en un subdirectorio.
Agregue el segundo repositorio como control remoto:
cd firstgitrepo/ git remote add secondrepo username@servername:andsoon
Asegúrate de haber descargado todas las confirmaciones de secondrepo:
git fetch secondrepo
Crea una sucursal local desde la segunda sucursal del repositorio:
git branch branchfromsecondrepo secondrepo/master
Mueva todos sus archivos a un subdirectorio:
git checkout branchfromsecondrepo mkdir subdir/ git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} subdir/ git commit -m "Moved files to subdir/"
Fusiona la segunda rama en la rama principal del primer repositorio:
git checkout master git merge --allow-unrelated-histories branchfromsecondrepo
Su repositorio tendrá más de una confirmación raíz, pero eso no debería representar un problema.
Esta función clonará el repositorio remoto en el directorio de repos local:
function git-add-repo
{
repo="$1"
dir="$(echo "$2" | sed ''s///$//'')"
path="$(pwd)"
tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed ''s/////g''| sed ''s//./_/g'')"
git clone "$repo" "$tmp"
cd "$tmp"
git filter-branch --index-filter ''
git ls-files -s |
sed "s,/t,&''"$dir"''/," |
GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
'' HEAD
cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"
}
Cómo utilizar:
cd current/package
git-add-repo https://github.com/example/example dir/to/save
¡Lucro!
Resulta que la respuesta es mucho más simple si simplemente intenta unir dos repositorios y hacer que parezca que fue así todo el tiempo en lugar de administrar una dependencia externa. Simplemente necesita agregar controles remotos a sus antiguos repositorios, combinarlos con su nuevo maestro, mover los archivos y carpetas a un subdirectorio, confirmar el movimiento y repetir para todos los repositorios adicionales. Los submódulos, las combinaciones de subárboles y las rebases de fantasía tienen la intención de resolver un problema ligeramente diferente y no son adecuados para lo que estaba tratando de hacer.
Aquí hay un script de Powershell de ejemplo para pegar dos repositorios juntos:
# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init
# Before we do a merge, we have to have an initial commit, so we''ll make a dummy commit
dir > deleteme.txt
git add .
git commit -m "Initial dummy commit"
# Add a remote for and fetch the old repo
git remote add -f old_a <OldA repo URL>
# Merge the files from old_a/master into new/master
git merge old_a/master --allow-unrelated-histories
# Clean up our dummy file because we don''t need it any more
git rm ./deleteme.txt
git commit -m "Clean up initial file"
# Move the old_a repo files and folders into a subdirectory so they don''t collide with the other repo coming later
mkdir old_a
dir -exclude old_a | %{git mv $_.Name old_a}
# Commit the move
git commit -m "Move old_a files into subdir"
# Do the same thing for old_b
git remote add -f old_b <OldB repo URL>
git merge old_b/master --allow-unrelated-histories
mkdir old_b
dir –exclude old_a,old_b | %{git mv $_.Name old_b}
git commit -m "Move old_b files into subdir"
Obviamente, en su lugar, podría fusionar old_b en old_a (que se convierte en el nuevo repositorio combinado) si prefiere hacerlo: modifique la secuencia de comandos para adaptarla.
Si desea traer ramas de características en progreso también, use esto:
# Bring over a feature branch from one of the old repos
git checkout -b feature-in-progress
git merge -s recursive -Xsubtree=old_a old_a/feature-in-progress
Esa es la única parte no obvia del proceso: no es una combinación de subárbol, sino un argumento para la fusión recursiva normal que le dice a Git que hemos cambiado el nombre del objetivo y eso ayuda a Git a alinear todo correctamente.
Escribí una explicación un poco más detallada here .
por favor, eche un vistazo al uso
git rebase --root --preserve-merges --onto
para vincular dos historias al principio de sus vidas.
Si tienes caminos que se superponen, arrégleselos con
git filter-branch --index-filter
cuando usa el registro, asegúrese de "encontrar copias más difíciles" con
git log -CC
de esa manera encontrarás cualquier movimiento de archivos en la ruta.