Actualice el submódulo Git a la última confirmación de origen


853

Tengo un proyecto con un submódulo Git. Es de un ssh: // ... URL, y está en commit A. El commit B ha sido enviado a ese URL, y quiero que el submódulo recupere el commit y lo cambie.

Ahora, entiendo que git submodule updatedebería hacer esto, pero no es así. No hace nada (sin salida, código de salida exitoso). Aquí hay un ejemplo:

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule 
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...

También he intentado git fetch mod, que parece hacer una zona de alcance (pero posiblemente no pueden, porque no se pida la contraseña!), Pero git logy git shownegar la existencia de nuevas confirmaciones. Hasta ahora solo he estado rmaplicando el módulo y volviéndolo a agregar, pero esto es incorrecto en principio y tedioso en la práctica.


55
La respuesta de David Z parece ser la mejor manera de hacerlo: ahora que Git tiene la funcionalidad que necesita incorporada a través de la --remoteopción, ¿tal vez sería útil marcar eso como la respuesta aceptada en lugar del enfoque "a mano" en la respuesta de Jason?
Mark Amery

1
Estoy muy de acuerdo con @MarkAmery. Si bien Jason dio una solución de trabajo, no es la forma prevista de hacerlo, ya que deja el puntero de confirmación del submódulo en el identificador de confirmación incorrecto. La nueva --remotees definitivamente una mejor solución en este momento, y dado que esta pregunta se ha vinculado desde un Github Gist sobre submódulos, creo que sería mejor para los lectores entrantes ver la nueva respuesta.
MutantOctopus

Buen toque con la hunter2contraseña: o)
lfarroco

Respuestas:


1458

El git submodule updatecomando en realidad le dice a Git que desea que sus submódulos verifiquen la confirmación ya especificada en el índice del superproyecto. Si desea actualizar sus submódulos a la última confirmación disponible desde su control remoto, deberá hacerlo directamente en los submódulos.

Entonces en resumen:

# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"

O, si eres una persona ocupada:

git submodule foreach git pull origin master

335
git submodule foreach git pull
Mathias Bynens el

87
@Nicklas En ese caso, use git submodule foreach git pull origin master.
Mathias Bynens

54
En este punto, con todas estas correcciones a las correcciones, necesito que alguien escriba una publicación explicativa del blog y me señale allí. Por favor.
Suz

25
pequeña mejora en el enfoque de "foreach" - es posible que desee agregar - recursivo allí en caso de que tenga submódulos dentro de submódulos. por lo que: git submodule foreach --recursive git pull origin master.
orion elenzil

44
@Abdull El -ainterruptor para git commit"Dígale al comando que organice automáticamente los archivos que han sido modificados y eliminados, pero los archivos nuevos que no le ha dicho a Git no se ven afectados".
godfrzero

473

Git 1.8.2 presenta una nueva opción --remote, que permitirá exactamente este comportamiento. Corriendo

git submodule update --remote --merge

buscará los últimos cambios desde la parte superior en cada submódulo, los combinará y verificará la última revisión del submódulo. Como dice la documentación :

--remoto

Esta opción solo es válida para el comando de actualización. En lugar de usar el SHA-1 grabado del superproyecto para actualizar el submódulo, use el estado de la rama de seguimiento remoto del submódulo.

Esto es equivalente a ejecutarse git pullen cada submódulo, que generalmente es exactamente lo que desea.


44
"equivalente a ejecutarse git pullen cada submódulo" Para aclarar, no hay diferencia (desde la perspectiva del usuario) entre su respuesta y git submodule foreach git pull?
Dennis

3
@Dennis hace esencialmente lo mismo, pero no estoy seguro de si la funcionalidad es exactamente la misma. Puede haber algunas diferencias menores que no conozco, por ejemplo, en la forma en que los dos comandos responden a alguna configuración.
David Z

55
Desearía poder votar este 10,000X. ¿Por qué no se muestra esto en la documentación de git en alguna parte? Enorme descuido.
serraosays

44
Para mí, en realidad diferían bastante significativamente; foreach git pullsolo los verificó, pero no actualizó el puntero del repositorio principal para apuntar a la confirmación más reciente del submódulo. Solo con --remoteeso hizo que apuntara a la última confirmación.
Ela782

55
¿Por qué la opción --merge? ¿Qué diferencia hace?
mFeinstein

127

En el directorio padre de su proyecto, ejecute:

git submodule update --init

O si tiene submódulos recursivos ejecute:

git submodule update --init --recursive

A veces esto todavía no funciona, porque de alguna manera tiene cambios locales en el directorio de submódulos local mientras se actualiza el submódulo.

La mayoría de las veces, el cambio local podría no ser el que desea cometer. Puede suceder debido a la eliminación de un archivo en su submódulo, etc.

git submodule update --init --recursive

55
Esta es la verdadera respuesta. ¿Puedo empujarlo a mi repositorio remoto de alguna manera?
MonsterMMORPG

¡Esto funciona para nuevos submódulos! Podría actualizar todos los demás, pero la carpeta de submódulos nuevos permanecería vacía hasta que ejecutara este comando.
Alexis Wilke

1
No extrae cambios para submódulos existentes
Sergey G.

73

Su proyecto principal apunta a un compromiso particular en el que debe estar el submódulo. git submodule updateintenta verificar esa confirmación en cada submódulo que se ha inicializado. El submódulo es realmente un repositorio independiente: solo crea una nueva confirmación en el submódulo y empujar eso no es suficiente. También debe agregar explícitamente la nueva versión del submódulo en el proyecto principal.

Entonces, en su caso, debe encontrar la confirmación correcta en el submódulo; supongamos que esa es la punta de master:

cd mod
git checkout master
git pull origin master

Ahora regrese al proyecto principal, organice el submódulo y confirme que:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

Ahora empuje su nueva versión del proyecto principal:

git push origin master

A partir de este momento, si alguien más actualiza su proyecto principal, entonces git submodule updateellos actualizarán el submódulo, suponiendo que se haya inicializado.


24

Parece que dos escenarios diferentes se mezclan en esta discusión:

escenario 1

Utilizando los punteros de mi repositorio principal para los submódulos, quiero verificar la confirmación en cada submódulo al que apunta el repositorio principal, posiblemente después de iterar primero a través de todos los submódulos y actualizar / extraerlos desde el control remoto.

Esto es, como se señaló, hecho con

git submodule foreach git pull origin BRANCH
git submodule update

Escenario 2, que creo que es a lo que apunta OP

Han ocurrido cosas nuevas en uno o más submódulos, y quiero 1) extraer estos cambios y 2) actualizar el repositorio principal para que apunte a la confirmación HEAD (más reciente) de este / estos submódulos.

Esto sería hecho por

git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH

No es muy práctico, ya que tendría que codificar n rutas a todos los n submódulos en, por ejemplo, un script para actualizar los punteros de confirmación del repositorio principal.

Sería genial tener una iteración automatizada a través de cada submódulo, actualizando el puntero del repositorio principal (usando git add) para apuntar a la cabeza del submódulo (s).

Para esto, hice este pequeño script Bash:

git-update-submodules.sh

#!/bin/bash

APP_PATH=$1
shift

if [ -z $APP_PATH ]; then
  echo "Missing 1st argument: should be path to folder of a git repo";
  exit 1;
fi

BRANCH=$1
shift

if [ -z $BRANCH ]; then
  echo "Missing 2nd argument (branch name)";
  exit 1;
fi

echo "Working in: $APP_PATH"
cd $APP_PATH

git checkout $BRANCH && git pull --ff origin $BRANCH

git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH

Para ejecutarlo, ejecuta

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

Elaboración

En primer lugar, supongo que la rama con el nombre $ BRANCH (segundo argumento) existe en todos los repositorios. Siéntase libre de hacer esto aún más complejo.

El primer par de secciones son algunas comprobaciones de que los argumentos están ahí. Luego saco las últimas cosas del repositorio principal (prefiero usar --ff (avance rápido) cada vez que solo hago pulls. Tengo rebase, BTW).

git checkout $BRANCH && git pull --ff origin $BRANCH

Entonces, puede ser necesario inicializar algunos submódulos, si se han agregado nuevos submódulos o aún no se han inicializado:

git submodule sync
git submodule init
git submodule update

Luego actualizo / extraigo todos los submódulos:

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

Observe algunas cosas: en primer lugar, estoy encadenando algunos comandos de Git usando &&, lo que significa que el comando anterior debe ejecutarse sin error.

Después de una posible extracción exitosa (si se encontraron cosas nuevas en el control remoto), hago un esfuerzo para asegurarme de que un posible compromiso de fusión no se quede atrás en el cliente. Nuevamente, solo sucede si un tirón realmente trajo cosas nuevas.

Finalmente, la final || truees asegurar que el script continúe con los errores. Para que esto funcione, todo en la iteración debe estar entre comillas dobles y los comandos Git están entre paréntesis (precedencia del operador).

Mi parte favorita:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

Itere todos los submódulos, con --quiet, lo que elimina la salida 'Entrar en MODULE_PATH'. Usando 'echo $path'(debe estar entre comillas simples), la ruta al submódulo se escribe en la salida.

Esta lista de rutas relativas de submódulos se captura en una matriz ( $(...)): finalmente repita esto y git add $iactualice el repositorio principal.

Finalmente, una confirmación con algún mensaje que explica que se actualizó el repositorio principal. Este commit será ignorado por defecto, si no se hizo nada. Empuje esto al origen, y ya está.

Tengo un script que ejecuta esto en un trabajo de Jenkins que luego se encadena a una implementación automática programada, y funciona de maravilla.

Espero que esto sea de ayuda para alguien.


2
! @ # $% SO Estamos usando scripts similares a los suyos; una nota: en lugar de `` `` git submodule foreach --quiet 'echo $ path' '`` usamos `` `` git submodule foreach --recursive --quiet pwd `` `dentro de los bucles for. El pwdcomando imprime la 'ruta absoluta' adecuada para cada submódulo presente; --recursiveasegura que visitamos todos los submódulos, incluidos los submódulos-dentro de submódulos -... que pueden estar presentes en un proyecto grande. Ambos métodos causan problemas con los directorios que incluyen espacios, por ejemplo, la /c/Users/Ger/Project\ Files/...política es nunca usar espacios en blanco en ningún lugar de nuestros proyectos.
Ger Hobbelt

2
Esto es bueno, y tienes razón en que hay un malentendido en algunas respuestas sobre cuál es la pregunta, pero como lo señala la excelente respuesta de David Z, tu script es innecesario ya que la funcionalidad se ha incorporado a Git desde mediados de 2013 cuando agregaron la --remoteopción. git submodule update --remotese comporta aproximadamente como lo hace tu script.
Mark Amery

@GerHobbelt Gracias. Tienes razón, solo tenemos 1 nivel de submódulos, así que nunca pensé en hacerlo recursivo. No actualizaré el script, antes de haber tenido la oportunidad de verificar que funciona como se esperaba, pero definitivamente mi script se someterá a submódulos secundarios. En cuanto a los espacios en las carpetas, esto definitivamente parece algo para evitar. : S
Frederik Struck-Schøning

@ MarkAmery Gracias por sus comentarios. Sin embargo, veo 1 problema: no por argumento poder especificar una rama para submódulos. Desde git manual: The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).no quiero editar .gitmodules ni .git / config cada vez que quiero hacer esto en otra rama que no sea master. Pero tal vez me he perdido algo? Además, el método parece imponer fusiones recursivas (por lo que se pierde la posibilidad de un avance rápido).
Frederik Struck-Schøning

Lo último: probé el método de @ DavidZ, y parece que no hace exactamente lo que me propuse hacer (y sobre qué preguntaba): Agregar el commit HEAD de submódulos a padre (es decir, "actualizar el puntero" ) Sin embargo, parece hacer el único trabajo muy bien (y más rápido) de buscar y fusionar los últimos cambios en todos los submódulos. Por desgracia, solo de forma predeterminada desde la rama maestra (a menos que edite el archivo .gitmodules (ver arriba)).
Frederik Struck-Schøning

19

Simple y simple, para buscar los submódulos:

git submodule update --init --recursive

Y ahora continúe actualizándolos a la última rama maestra (por ejemplo):

git submodule foreach git pull origin master

13

Tenga en cuenta que, aunque la forma moderna de actualizar las confirmaciones de submódulos sería:

git submodule update --recursive --remote --merge --force

La forma más antigua era:

git submodule foreach --quiet git pull --quiet origin

Excepto ... esta segunda forma no es realmente "tranquila".

Ver commit a282f5a (12 abr 2019) por Nguyễn Thái Ngọc Duy ( pclouds) .
(Fusionada por Junio ​​C Hamano - gitster- en commit f1c9f6c , 25 abr 2019)

submodule foreach: arreglo " <command> --quiet" no se respeta

Robin informó que

git submodule foreach --quiet git pull --quiet origin

ya no está realmente tranquilo.
Debería estar en silencio antes de fc1b924 ( submodule: submodulesubcomando de puerto ' foreach' desde shell a C, 2018-05-10, Git v2.19.0-rc0) porque parseoptno puede comer opciones accidentalmente en ese momento.

" git pull" se comporta como si --quietno se hubiera dado.

Esto sucede porque parseoptin submodule--helperintentará analizar ambas --quietopciones como si fueran las opciones de foreach, no git-pulllas de.
Las opciones analizadas se eliminan de la línea de comando. Entonces, cuando tiramos más tarde, ejecutamos solo esto

git pull origin

Al llamar al asistente de submódulo, agregar " --" delante de " git pull" se detendrá parseoptpara analizar las opciones que realmente no pertenecen submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWNse elimina como medida de seguridad. parseoptnunca debería ver opciones desconocidas o algo ha salido mal. También hay un par de actualizaciones de cadenas de uso mientras las estoy mirando.

Mientras lo hago, también agrego " --" a otros subcomandos que pasan " $@" a submodule--helper. " $@" en estos casos son caminos y es menos probable que lo sean --something-like-this.
Pero el punto sigue en pie, git-submoduleha analizado y clasificado cuáles son las opciones, cuáles son los caminos.
submodule--helperNunca debería considerar las rutas pasadas git-submodulecomo opciones, incluso si se parecen a una.


Y Git 2.23 (Q3 2019) soluciona otro problema: " git submodule foreach" no protegió las opciones de línea de comando que se pasaron al comando para ejecutarse correctamente en cada submódulo cuando la --recursiveopción " " estaba en uso.

Ver commit 30db18b (24 de junio de 2019) por Morian Sonnet ( momoson) .
(Fusión por Junio ​​C Hamano - gitster- en commit 968eecb , 09 jul 2019)

submodule foreach: arregla la recurrencia de opciones

Vocación:

git submodule foreach --recursive <subcommand> --<option>

conduce a un error que indica que la opción --<option>es desconocida para submodule--helper.
Eso es, por supuesto, solo, cuando <option>no es una opción válida para git submodule foreach.

La razón de esto es que la llamada anterior se traduce internamente en una llamada al submódulo - ayudante:

git submodule--helper foreach --recursive \
    -- <subcommand> --<option>

Esta llamada comienza ejecutando el subcomando con su opción dentro del submódulo de primer nivel y continúa llamando a la siguiente iteración de la submodule foreachllamada

git --super-prefix <submodulepath> submodule--helper \
   foreach --recursive <subcommand> --<option>

dentro del submódulo de primer nivel. Tenga en cuenta que falta el doble guión frente al subcomando.

Este problema comienza a surgir recientemente, ya que el PARSE_OPT_KEEP_UNKNOWNindicador para el análisis de argumentos git submodule foreachse eliminó en commit a282f5a .
Por lo tanto, la opción desconocida se queja ahora, ya que el análisis doble de argumentos no termina correctamente.

Esta confirmación corrige el problema al agregar el doble guión frente al subcomando durante la recursión.


7
git pull --recurse-submodules

Esto extraerá todos los últimos commits.


4

En mi caso, quería gitactualizar a la última versión y, al mismo tiempo, volver a llenar los archivos faltantes.

Lo siguiente restauró los archivos faltantes (gracias a los --forcecuales no parece haber sido mencionado aquí), pero no obtuvo ninguna confirmación nueva:

git submodule update --init --recursive --force

Esto hizo:

git submodule update --recursive --remote --merge --force


3

@ Jason es correcto en cierto modo, pero no del todo.

actualizar

Actualice los submódulos registrados, es decir, clone los submódulos faltantes y verifique la confirmación especificada en el índice del repositorio contenedor. Esto hará que los submódulos HEAD se desconecten a menos que se especifique --rebase o --merge o que el submódulo clave.

Entonces, git submodule updaterealiza el pago, pero es para confirmar en el índice del repositorio que contiene. Todavía no conoce el nuevo compromiso en sentido ascendente. Vaya a su submódulo, obtenga la confirmación que desea y confirme el estado del submódulo actualizado en el repositorio principal y luego haga lo git submodule update.


1
Parece que si muevo el submódulo a una confirmación diferente y luego la ejecuto git submodule update, la actualización moverá el submódulo a la confirmación especificada en el HEAD actual del superproyecto. (cualquiera que sea la confirmación más reciente en el superproyecto dice que el subproyecto debería estar en - este comportamiento, después de la explicación en la publicación de Jason, me parece lógico) También parece buscar, pero solo en el caso de que el subproyecto esté en la confirmación incorrecta , lo que se sumaba a mi confusión.
Thanatos

2

Aquí hay una frase increíble para actualizar todo a lo último en master:

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Gracias a Mark Jaquith


2

Si no conoce la rama host, haga esto:

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

Obtendrá una rama del repositorio principal de Git y luego, para cada submódulo, extraerá la misma rama.


0

Si está buscando la masterrama de pago para cada submódulo, puede usar el siguiente comando para ese propósito:

git submodule foreach git checkout master
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.