¿Cómo des-submodular un submódulo Git?

16 minutos de lectura

¿Como des submodular un submodulo Git
Zorrorojoveloz

¿Cuáles son las mejores prácticas para desmodular un submódulo de Git y devolver todo el código al repositorio central?

  • Nota: con git1.8.3, ahora puedes probar un git submodule deinitmira mi respuesta a continuación

    – VoC

    23 de abril de 2013 a las 6:13

  • Puedo malinterpretarlo, pero git submodule deinit parece eliminar el código.

    – Joe Germuska

    21/09/2013 a las 12:50

  • Desde git 1.8.5 (noviembre de 2013), un simple git submodule deinit asubmodule ; git rm asubmodule es suficiente, como se ilustra en mi respuesta a continuación

    – VoC

    2 de marzo de 2014 a las 11:08

  • considere usar el subárbol git

    – laplasz

    13 de diciembre de 2019 a las 12:57

1646966419 268 ¿Como des submodular un submodulo Git
gimnasia

Si todo lo que desea es colocar el código de su submódulo en el repositorio principal, solo necesita eliminar el submódulo y volver a agregar los archivos en el repositorio principal:

git rm --cached submodule_path # delete reference to submodule HEAD (no trailing slash)
git rm .gitmodules             # if you have more than one submodules,
                               # you need to edit this file instead of deleting!
rm -rf submodule_path/.git     # make sure you have backup!!
git add submodule_path         # will add files instead of commit reference
git commit -m "remove submodule"

Si también desea conservar el historial del submódulo, puede hacer un pequeño truco: “combinar” el submódulo en el repositorio principal, de modo que el resultado sea el mismo que antes, excepto que los archivos del submódulo ahora están en el repositorio principal.

En el módulo principal deberá hacer lo siguiente:

# Fetch the submodule commits into the main repository
git remote add submodule_origin git://url/to/submodule/origin
git fetch submodule_origin

# Start a fake merge (won't change any files, won't commit anything)
git merge -s ours --no-commit submodule_origin/master

# Do the same as in the first solution
git rm --cached submodule_path # delete reference to submodule HEAD
git rm .gitmodules             # if you have more than one submodules,
                               # you need to edit this file instead of deleting!
rm -rf submodule_path/.git     # make sure you have backup!!
git add submodule_path         # will add files instead of commit reference

# Commit and cleanup
git commit -m "removed submodule"
git remote rm submodule_origin

El repositorio resultante se verá un poco extraño: habrá más de una confirmación inicial. Pero no causará ningún problema para Git.

Una gran ventaja de esta segunda solución es que aún puede ejecutar git blame o git log en los archivos que originalmente estaban en submódulos. De hecho, lo que sucede aquí es solo un cambio de nombre de muchos archivos dentro de un repositorio, y Git debería detectarlo automáticamente. Si todavía tienes problemas con git logpruebe algunas opciones (p. ej., --follow, -M, -C) que mejoran el cambio de nombre y la detección de copias.

  • Creo que necesito hacer su segundo método (preservar el historial) en algunos repositorios de git que tengo. ¿Podría explicar qué parte de los comandos anteriores hace que los archivos del submódulo terminen en el subdirectorio? ¿Es que cuando haces la combinación, git trae el archivo en el directorio de nivel superior (con su historial) pero cuando haces el git agrega submodule_path implícitamente hace un git mv para cada archivo?

    – Bowie Owens

    20 de diciembre de 2011 a las 4:43

  • Básicamente sí. El truco es que git no almacena las operaciones de cambio de nombre: en cambio, las detecta mirando las confirmaciones principales. Si hay un contenido de archivo que estaba presente en la confirmación anterior, pero con un nombre de archivo diferente, se considera un cambio de nombre (o copia). En los pasos anteriores, git merge asegura que habrá un “compromiso previo” para cada archivo (en uno de los dos “lados” de la fusión).

    – gimnasio

    26 de diciembre de 2011 a las 13:44


  • Gracias gyim, comencé un proyecto en el que pensé que tenía sentido dividir las cosas en un par de repositorios y vincularlos nuevamente con submódulos. Pero ahora parece demasiado diseñado y quiero combinarlos de nuevo sin perder mi historial.

    – Bowie Owens

    27 de diciembre de 2011 a las 23:32

  • @theduke También tuve este problema. Se puede solucionar, antes de seguir estos pasos, moviendo todos los archivos de su repositorio de submódulos a una estructura de directorio con el mismo camino como el repositorio en el que está a punto de fusionarse: es decir. si su submódulo en el repositorio principal está en foo/, en el submódulo, realice mkdir foo && git mv !(foo) foo && git commit.

    – Chris Abajo

    11 de octubre de 2013 a las 7:27


  • Necesario agregar --allow-unrelated-histories para forzar la fusión en la fusión falsa como estaba recibiendo fatal: refusing to merge unrelated historiesmás aquí: github.com/git/git/blob/master/Documentación/RelNotes/…

    – Vaskort

    15 de agosto de 2016 a las 13:23


¿Como des submodular un submodulo Git
jsears

Creé un script que traducirá un submódulo a un directorio simple, mientras retiene todo el historial de archivos. no sufre de la git log --follow <file> problemas que sufren las otras soluciones. También es una invocación de una sola línea muy fácil que hace todo el trabajo por ti. Buena suerte.

Se basa en el excelente trabajo de Lucas Jenß, descrito en su entrada de blog “Integración de un submódulo en el repositorio principal“, pero automatiza todo el proceso y limpia algunos otros casos de esquina.

El código más reciente se mantendrá con correcciones de errores en github en https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewritepero por el bien del protocolo de respuesta de stackoverflow adecuado, he incluido la solución en su totalidad a continuación.

Uso:

$ git-submodule-rewrite <submodule-name>

git-submódulo-reescritura:

#!/usr/bin/env bash

# This script builds on the excellent work by Lucas Jenß, described in his blog
# post "Integrating a submodule into the parent repository", but automates the
# entire process and cleans up a few other corner cases.
# https://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html

function usage() {
  echo "Merge a submodule into a repo, retaining file history."
  echo "Usage: $0 <submodule-name>"
  echo ""
  echo "options:"
  echo "  -h, --help                Print this message"
  echo "  -v, --verbose             Display verbose output"
}

function abort {
    echo "$(tput setaf 1)$1$(tput sgr0)"
    exit 1
}

function request_confirmation {
    read -p "$(tput setaf 4)$1 (y/n) $(tput sgr0)"
    [ "$REPLY" == "y" ] || abort "Aborted!"
}

function warn() {
  cat << EOF
    This script will convert your "${sub}" git submodule into
    a simple subdirectory in the parent repository while retaining all
    contents and file history.

    The script will:
      * delete the ${sub} submodule configuration from .gitmodules and
        .git/config and commit it.
      * rewrite the entire history of the ${sub} submodule so that all
        paths are prefixed by ${path}.
        This ensures that git log will correctly follow the original file
        history.
      * merge the submodule into its parent repository and commit it.

    NOTE: This script might completely garble your repository, so PLEASE apply
    this only to a fresh clone of the repository where it does not matter if
    the repo is destroyed.  It would be wise to keep a backup clone of your
    repository, so that you can reconstitute it if need be.  You have been
    warned.  Use at your own risk.

EOF

  request_confirmation "Do you want to proceed?"
}

function git_version_lte() {
  OP_VERSION=$(printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' '\n' | head -n 4))
  GIT_VERSION=$(git version)
  GIT_VERSION=$(printf "%03d%03d%03d%03d" $(echo "${GIT_VERSION#git version}" | tr '.' '\n' | head -n 4))
  echo -e "${GIT_VERSION}\n${OP_VERSION}" | sort | head -n1
  [ ${OP_VERSION} -le ${GIT_VERSION} ]
}

function main() {

  warn

  if [ "${verbose}" == "true" ]; then
    set -x
  fi

  # Remove submodule and commit
  git config -f .gitmodules --remove-section "submodule.${sub}"
  if git config -f .git/config --get "submodule.${sub}.url"; then
    git config -f .git/config --remove-section "submodule.${sub}"
  fi
  rm -rf "${path}"
  git add -A .
  git commit -m "Remove submodule ${sub}"
  rm -rf ".git/modules/${sub}"

  # Rewrite submodule history
  local tmpdir="$(mktemp -d -t submodule-rewrite-XXXXXX)"
  git clone "${url}" "${tmpdir}"
  pushd "${tmpdir}"
  local tab="$(printf '\t')"
  local filter="git ls-files -s | sed \"s/${tab}/${tab}${path}\//\" | GIT_INDEX_FILE=\${GIT_INDEX_FILE}.new git update-index --index-info && mv \${GIT_INDEX_FILE}.new \${GIT_INDEX_FILE}"
  git filter-branch --index-filter "${filter}" HEAD
  popd

  # Merge in rewritten submodule history
  git remote add "${sub}" "${tmpdir}"
  git fetch "${sub}"

  if git_version_lte 2.8.4
  then
    # Previous to git 2.9.0 the parameter would yield an error
    ALLOW_UNRELATED_HISTORIES=""
  else
    # From git 2.9.0 this parameter is required
    ALLOW_UNRELATED_HISTORIES="--allow-unrelated-histories"
  fi

  git merge -s ours --no-commit ${ALLOW_UNRELATED_HISTORIES} "${sub}/master"
  rm -rf tmpdir

  # Add submodule content
  git clone "${url}" "${path}"
  rm -rf "${path}/.git"
  git add "${path}"
  git commit -m "Merge submodule contents for ${sub}"
  git config -f .git/config --remove-section "remote.${sub}"

  set +x
  echo "$(tput setaf 2)Submodule merge complete. Push changes after review.$(tput sgr0)"
}

set -euo pipefail

declare verbose=false
while [ $# -gt 0 ]; do
    case "$1" in
        (-h|--help)
            usage
            exit 0
            ;;
        (-v|--verbose)
            verbose=true
            ;;
        (*)
            break
            ;;
    esac
    shift
done

declare sub="${1:-}"

if [ -z "${sub}" ]; then
  >&2 echo "Error: No submodule specified"
  usage
  exit 1
fi

shift

if [ -n "${1:-}" ]; then
  >&2 echo "Error: Unknown option: ${1:-}"
  usage
  exit 1
fi

if ! [ -d ".git" ]; then
  >&2 echo "Error: No git repository found.  Must be run from the root of a git repository"
  usage
  exit 1
fi

declare path="$(git config -f .gitmodules --get "submodule.${sub}.path")"
declare url="$(git config -f .gitmodules --get "submodule.${sub}.url")"

if [ -z "${path}" ]; then
  >&2 echo "Error: Submodule not found: ${sub}"
  usage
  exit 1
fi

if ! [ -d "${path}" ]; then
  >&2 echo "Error: Submodule path not found: ${path}"
  usage
  exit 1
fi

main

  • No funciona en Ubuntu 16.04. yo envié una solicitud de extracción al repositorio de Github.

    – qznc

    3 de mayo de 2017 a las 11:09

  • Buena captura, @qznc. Esto fue probado en OSX. Con mucho gusto fusionaré eso cuando pase en ambas plataformas.

    – jsears

    3 mayo 2017 a las 15:09


  • Se fusionó la compatibilidad con @qznc Ubuntu 16.04 y se actualizó la respuesta.

    – jsears

    12 mayo 2017 a las 21:00

  • Esta es la mejor respuesta, mantiene toda la historia. ¡Muy agradable!

    – Carlos B

    8 de enero de 2018 a las 11:28

  • Haga todo el trabajo sin errores en Git Bash 2.20.1.1 en Windows 10 con la última versión de github: curl https://raw.githubusercontent.com/jeremysears/scripts/master/bin/git-submodule-rewrite > git-submodule-rewrite.sh y ./git-submodule-rewrite.sh <submodule-name>

    – Alexei

    6 de diciembre de 2019 a las 15:35


1646966421 47 ¿Como des submodular un submodulo Git
VonC

Ya que git 1.8.5 (noviembre de 2013)) (sin guardar el historial del submódulo):

mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule

Esa voluntad:

  • darse de baja y descargar (es decir eliminar el contenido de) el submódulo (deinitpor lo tanto, la mv primero),
  • limpiar el .gitmodules para ti (rm),
  • y elimine la entrada especial que representa ese submódulo SHA1 en el índice del repositorio principal (rm).

Una vez que se completa la eliminación del submódulo (deinit y git rm), puede cambiar el nombre de la carpeta a su nombre original y agregarla al repositorio de git como una carpeta normal.

Nota: si el submódulo fue creado por un Git antiguo (< 1.8), es posible que deba eliminar el anidado .git carpeta dentro del propio submódulo, como comentó Simon East


Si necesita mantener el historial del submódulo, vea la respuesta de jsears, que usa git filter-branch.

  • Esto realmente lo elimina del árbol de trabajo en 1.8.4 (se borró todo el directorio de mi submódulo).

    – Chris Abajo

    11 de octubre de 2013 a las 7:07


  • @ChrisDown quieres decir, el deinit solo limpió el árbol de trabajo de su submódulo?

    – VoC

    11 de octubre de 2013 a las 7:40

  • Sí, elimina todo el contenido del directorio del submódulo.

    – Chris Abajo

    11 de octubre de 2013 a las 7:47

  • @mschuett no, no te estás perdiendo nada: un submódulo no tiene un .git en primer lugar. Si ese fuera su caso, era un repositorio anidado, no un submódulo. Eso explica por qué esta respuesta anterior no se aplicaría en su caso. Para conocer la diferencia entre los dos, consulte stackoverflow.com/a/34410102/6309.

    – VoC

    13/03/2016 a las 20:14


  • @VonC Actualmente estoy en 2.9.0.windows.1, sin embargo, es posible que los submódulos se hayan creado hace varios años en una versión mucho más antigua de git, no estoy seguro. Creo que los pasos parecen funcionar siempre y cuando elimine ese archivo antes de hacer el último add + commit.

    – Simón Este

    30 de junio de 2017 a las 5:12


¿Como des submodular un submodulo Git
Marcel Jackwerth

  1. git rm --cached the_submodule_path
  2. Retire la sección del submódulo de la .gitmodules archivo, o si es el único submódulo, elimine el archivo.
  3. hacer una confirmación de “submódulo eliminado xyz”
  4. git add the_submodule_path
  5. otra confirmación “código base agregado de xyz”

No encontré ninguna manera más fácil todavía. Puede comprimir 3-5 en un solo paso a través de git commit -a – cuestion de gusto.

1646966422 798 ¿Como des submodular un submodulo Git
comiendo la noche

Hay muchas respuestas aquí, pero todas parecen ser demasiado complejas y es probable que no hagan lo que usted quiere. Estoy seguro de que la mayoría de la gente quiere mantener su historial.

Para este ejemplo, el repositorio principal será git@site.com:main/main.git y el repositorio del submódulo será git@site.com:main/child.git. Esto supone que el submódulo está ubicado en el directorio raíz del repositorio principal. Ajuste las instrucciones según sea necesario.

Comience por clonar el repositorio principal y elimine el submódulo anterior.

git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"

Ahora agregaremos los repositorios secundarios aguas arriba al repositorio principal.

git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master

El siguiente paso supone que desea mover los archivos en la rama merge-prep a la misma ubicación que el submódulo anterior, aunque puede cambiar fácilmente la ubicación cambiando la ruta del archivo.

mkdir child

mueva todas las carpetas y archivos excepto la carpeta .git a la carpeta secundaria.

git add --all
git commit -m "merge prep"

Ahora puede simplemente fusionar sus archivos nuevamente en la rama principal.

git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required 

Mire a su alrededor y asegúrese de que todo se vea bien antes de correr git push

Lo único que debe recordar ahora es que git log no sigue de forma predeterminada los archivos movidos, sin embargo, al ejecutar git log --follow filename puede ver el historial completo de sus archivos.

  • llegué hasta la final git merge merge-prep y recibi el error fatal: refusing to merge unrelated histories. La solución es esta: git merge --allow-unrelated-histories merge-prep.

    – humilde hacker

    28 de diciembre de 2016 a las 18:46

  • @humblehacker gracias, agregué un pequeño comentario en caso de que otros también se encuentren con esto.

    – comiendo la noche

    29 de diciembre de 2016 a las 0:35

  • La mejor respuesta para mantener el historial del submódulo. Gracias @mschuett

    – Antón Temchenko

    7 de noviembre de 2017 a las 10:48

  • En el ejemplo aquí, ¿hay alguna forma de obtener los archivos del flujo ascendente en el child directorio, para no tener que moverlos más tarde? Tengo los mismos nombres de archivo en un submódulo y en el repositorio principal… así que solo aparece un conflicto de fusión ya que está tratando de fusionar los dos archivos.

    – Skiterm

    1 de enero de 2018 a las 23:24

  • Posiblemente, pero no lo sé de antemano. Personalmente, solo haría un compromiso moviendo los archivos en el repositorio que está tratando de mover para que residan en el directorio que desea antes de extraerlos.

    – comiendo la noche

    3 de enero de 2018 a las 16:54

Nos pasó que creamos 2 repositorios para 2 proyectos que estaban tan acoplados que no tenía ningún sentido tenerlos separados, así que los fusionamos.

Primero mostraré cómo fusionar las ramas maestras en cada una y luego explicaré cómo puedes extender esto a todas las ramas que tienes, espero que te ayude.

Si tiene el submódulo funcionando y desea convertirlo en un directorio en su lugar, puede hacerlo:

git clone project_uri project_name

Aquí hacemos un clon limpio para que funcione. Para este proceso no necesita inicializar o actualizar los submódulos, así que sáltelo.

cd project_name
vim .gitmodules

Editar .gitmodules con su editor favorito (o Vim) para eliminar el submódulo que planea reemplazar. Las líneas que necesita eliminar deberían verse así:

[submodule "lib/asi-http-request"]
    path = lib/asi-http-request
    url = https://github.com/pokeb/asi-http-request.git

Después de guardar el archivo,

git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule

Aquí eliminamos la relación del submódulo por completo para que podamos traer el otro repositorio al proyecto en el lugar.

git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master

Aquí buscamos el repositorio de submódulos para fusionar.

git merge -s ours --no-commit submodule_origin/master

Aquí comenzamos una operación de fusión de los 2 repositorios, pero nos detenemos antes de confirmar.

git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master

Aquí enviamos el contenido de master en el submódulo al directorio donde estaba antes de anteponer un nombre de directorio

git commit -am "submodule_name is now part of main project"

Aquí completamos el procedimiento haciendo un compromiso de los cambios en la fusión.

Después de terminar esto, puede presionar y comenzar de nuevo con cualquier otra rama para fusionar, simplemente verifique la rama en su repositorio que recibirá los cambios y cambie la rama que trae en las operaciones de fusión y lectura del árbol.

  • llegué hasta la final git merge merge-prep y recibi el error fatal: refusing to merge unrelated histories. La solución es esta: git merge --allow-unrelated-histories merge-prep.

    – humilde hacker

    28 de diciembre de 2016 a las 18:46

  • @humblehacker gracias, agregué un pequeño comentario en caso de que otros también se encuentren con esto.

    – comiendo la noche

    29 de diciembre de 2016 a las 0:35

  • La mejor respuesta para mantener el historial del submódulo. Gracias @mschuett

    – Antón Temchenko

    7 de noviembre de 2017 a las 10:48

  • En el ejemplo aquí, ¿hay alguna forma de obtener los archivos del flujo ascendente en el child directorio, para no tener que moverlos más tarde? Tengo los mismos nombres de archivo en un submódulo y en el repositorio principal… así que solo aparece un conflicto de fusión ya que está tratando de fusionar los dos archivos.

    – Skiterm

    1 de enero de 2018 a las 23:24

  • Posiblemente, pero no lo sé de antemano. Personalmente, solo haría un compromiso moviendo los archivos en el repositorio que está tratando de mover para que residan en el directorio que desea antes de extraerlos.

    – comiendo la noche

    3 de enero de 2018 a las 16:54

1646966422 788 ¿Como des submodular un submodulo Git
lucas h

La mejor respuesta a esto que he encontrado está aquí:

http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html

Este artículo explica muy bien el procedimiento.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad