¿Por qué mi Git Submodule HEAD está separada del maestro?

163

Estoy usando submódulos Git. Después de extraer los cambios del servidor, muchas veces la cabeza de mi submódulo se separa de la rama maestra.

¿Por que sucede?

Tengo que hacer siempre:

git branch
git checkout master

¿Cómo puedo asegurarme de que mi submódulo siempre apunte a la rama maestra?

om471987
fuente
1
¿Leíste esta respuesta? stackoverflow.com/questions/1777854/…
Johnny Z
@bitoiu Miré el subárbol y Google Repo. Todavía no tengo la solución perfecta :(
om471987
1
mi experiencia con gitsubmodules, en un entorno de CI es horrible, tal vez otras personas tengan mejores experiencias.
bitoiu
@JohnnyZ Gracias. Comprendí que el submódulo apunta a una confirmación y no a la cabeza del árbol. Pero por qué se separó de la rama. Si tiene una rama, ¿no debería estar adjuntada por defecto
Om471987
3
No se apresure a descartar submódulos solo porque escuchó que son malos. Son una solución deficiente si desea una integración continua, pero son una solución casi perfecta si desea incrustar código de un proyecto externo y administra explícitamente todos los tirones. Esta es a menudo la mejor práctica si se está integrando con un módulo no bifurcado que no está controlado por su organización. El problema es que son una solución tentadora en todo tipo de situaciones en las que no funcionan muy bien. El mejor consejo es leer cómo funcionan y evaluar su escenario.
Sarah G

Respuestas:

176

EDITAR:

Ver @Simba Answer para una solución válida

submodule.<name>.updatees lo que desea cambiar, consulte los documentos : predeterminado,checkout
submodule.<name>.branch especifique la rama remota a rastrear: predeterminadomaster


ANTIGUA RESPUESTA:

Personalmente, odio las respuestas aquí que dirigen a enlaces externos que pueden dejar de funcionar con el tiempo y compruebo mi respuesta aquí (a menos que la pregunta sea duplicada) : dirigir a la pregunta que cubre el tema entre las líneas de otro tema, pero en general es igual a: "Estoy sin contestar, lea la documentación ".

Volviendo a la pregunta: ¿por qué sucede?

Situación que describiste

Después de extraer los cambios del servidor, muchas veces la cabeza de mi submódulo se separa de la rama maestra.

Este es un caso común cuando uno no usa submódulos con demasiada frecuencia o acaba de comenzar con submódulos . Creo que estoy en lo correcto al afirmar que todos hemos estado allí en algún momento donde la CABEZA de nuestro submódulo se separa.

  • Causa: su submódulo no está rastreando la rama correcta (maestro predeterminado).
    Solución: asegúrese de que su submódulo rastrea la rama correcta
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • Causa: su repositorio principal no está configurado para rastrear la rama de submódulos.
    Solución: Haga que su submódulo rastree su rama remota agregando nuevos submódulos con los siguientes dos comandos.
    • Primero le dices a git que rastree tu control remoto <branch>.
    • le dices a git que realice un rebase o una fusión en lugar de pagar
    • le dices a git que actualice tu submódulo desde el control remoto.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Si no ha agregado su submódulo existente de esta manera, puede solucionarlo fácilmente:
    • Primero, debe asegurarse de que su submódulo tenga la rama verificada de la que desea realizar un seguimiento.
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

En los casos comunes, ya ha reparado su CABEZA DESCONECTADA ya que estaba relacionada con uno de los problemas de configuración anteriores.

Fijación de la cabeza separada cuando .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

Pero si logró realizar algunos cambios localmente para el submódulo y se comprometió, los empujó al control remoto y luego cuando ejecutó 'git checkout', Git le notifica:

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

La opción recomendada para crear una rama temporal puede ser buena, y luego puede fusionar estas ramas, etc. Sin embargo, personalmente lo usaría solo git cherry-pick <hash>en este caso.

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

Aunque hay algunos casos más en los que puede poner sus submódulos en el estado HEAD DETACHED, espero que ahora entienda un poco más cómo depurar su caso particular.

mkungla
fuente
2
HEAD detached es el comportamiento predeterminado de git submodule update --remote. Eche un vistazo a la respuesta de Simba, creo que esa debería ser la respuesta correcta.
magomar
78

Adición de una branchopción en la .gitmoduleque se NO relacionado con el comportamiento individual de los submódulos en absoluto. La antigua respuesta de @mkungla es incorrecta u obsoleta.

De git submodule --help, HEAD detached es el comportamiento predeterminado degit submodule update --remote .

Primero, no hay necesidad de especificar una rama para rastrear . origin/masteres la rama predeterminada que se debe rastrear.

--remoto

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. El control remoto utilizado es el control remoto de la rama ( branch.<name>.remote), cuyo valor predeterminado esorigin . La rama remota usó los valores predeterminadosmaster .

Por qué

Entonces, ¿por qué se desprende HEAD después update? Esto es causado por el módulo de comportamiento de actualización predeterminado:checkout .

--revisa

Verifique la confirmación registrada en el superproyecto en un HEAD separado en el submódulo. Este es el comportamiento predeterminado , el uso principal de esta opción es anular submodule.$name.updatecuando se establece en un valor distinto de checkout.

Para explicar este extraño comportamiento de actualización, debemos entender cómo funcionan los submódulos.

Cita de Comenzando con submódulos en el libro Pro Git

Aunque sbmodule DbConnectores un subdirectorio en su directorio de trabajo, Git lo ve como un submódulo y no rastrea su contenido cuando no está en ese directorio. En cambio, Git lo ve como una confirmación particular de ese repositorio .

El repositorio principal rastrea el submódulo con su estado en un punto específico , el id de confirmación . Entonces, cuando actualiza los módulos, está actualizando la identificación de confirmación a una nueva.

Cómo

Si desea que el submódulo se fusione con la rama remota automáticamente, use --mergeo --rebase.

--unir

Esta opción solo es válida para el comando de actualización . Combine la confirmación registrada en el superproyecto en la rama actual del submódulo. Si se da esta opción, el HEAD del submódulo no se separará .

--rebase

Rebase la rama actual en el commit registrado en el superproyecto. Si se da esta opción, el HEAD del submódulo no se separará .

Todo lo que necesitas hacer es

git submodule update --remote --merge
# or
git submodule update --remote --rebase

Alias ​​recomendado:

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

También hay una opción para hacer --mergeo --rebasecomo el comportamiento predeterminado de git submodule update, mediante la configuración submodule.$name.updatede mergeo rebase.

Aquí hay un ejemplo sobre cómo configurar el comportamiento de actualización predeterminado de la actualización de submódulos en .gitmodule.

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

O configurarlo en la línea de comando,

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

Referencias

Simba
fuente
66
Yo uso git submodule update --remote --merge, y tira hacia abajo el submódulo en un estado separado. También probé --rebasecon el mismo resultado.
Joe Strout
8
@JoeStrout Si su submódulo ya se ha desconectado, corrija el estado desconectado antes de realizar una actualización con los comandos anteriores. cden el submódulo, pago y envío el submódulo a una rama específica con, git checkout master.
Simba
2
O, si esto es demasiado complicado para múltiples submódulos (recursivos), simplemente hazlo git submodule foreach --recursive git checkout master.
stefanct
1
Solo entiendo parcialmente las descripciones de "cómo funciona git". TBH No estoy realmente interesado en entender cómo funciona git, solo quiero usarlo. Ahora entiendo que puedo arreglar submódulos separados con git submodule foreach --recursive git checkout master. Pero, ¿cómo puedo evitar que git los separe siempre? ¡Establecer opciones de configuración para cada submódulo no es una opción!
Nicolas
Para mí, la ejecución git submodule update --remote --mergeno dejó el submódulo en un estado HEAD separado, sino que se ejecutó git submodule updatedespués de editar mi .gitmodulearchivo, ya que indicó que sí dejó el submódulo en un estado HEAD separado.
bweber13
41

Me cansé de que siempre se separe, así que solo uso un script de shell para construirlo para todos mis módulos. Supongo que todos los submódulos están en master: aquí está el script:

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

ejecutarlo desde su módulo padre

j2emanue
fuente
2
git submodule foreach git pull origin master - esto es lo que estaba buscando .. kudos
csomakk
simple y conciso! ¡Gracias!
zhekaus
12

Mira mi respuesta aquí: submódulos Git: especifica una rama / etiqueta

Si lo desea, puede agregar la línea "branch = master" en su archivo .gitmodules manualmente. Lea el enlace para ver a qué me refiero.

EDITAR: para realizar un seguimiento de un proyecto de submódulo existente en una sucursal, siga las instrucciones de VonC aquí:

Submódulos Git: especifique una rama / etiqueta

Johnny Z
fuente
14
Se supone que las respuestas deben estar en línea; El enlace IIRC a las respuestas es un paso en falso de Stack Overflow.
Tony Topper
1
@TonyTopper ¿Incluso cuando solo se vincula a otra respuesta SO? IIRC solo desaprueba los enlaces externos, ya que pueden desaparecer y luego el enlace está inactivo y la respuesta es, bueno, inútil. Sin embargo, no hay tal peligro con las respuestas SO, nunca desaparecerán, a menos que SO desaparezca (y se pueden restaurar sin importar lo que suceda). También ha respondido la pregunta, ya branch = master" line into your .gitmoduleque de hecho es la respuesta completa, resolvió ese problema para mí.
Mecki
9

La otra forma de hacer que su submódulo revise la rama es ir al .gitmodulesarchivo en la carpeta raíz y agregar el campo branchen la configuración del módulo de la siguiente manera:

branch = <branch-name-you-want-module-to-checkout>

frontendgirl
fuente
15
Para mí esto no funciona. Lo he configurado correctamente branch = my_wanted_branch. Pero ejecutarlo git submodule update --remotetodavía se verifica como cabeza separada.
Andrius
Haga esto, luego cd sudmodule & git co thebranche & cd .., luego git submodule update --remote y ¡funciona!
pdem
¿No es así como '.gitmodules' está bajo uso activo (se está leyendo) solo mientras el superproyecto se está clonando en submódulos de forma recursiva o se está inicializando el submódulo? En otras palabras, su propio repositorio actualiza el archivo en el que no siempre se beneficia de las actualizaciones de configuración de submódulos puestas a '.gitmodules'. Según tengo entendido, '.gitmodules' es una plantilla para la configuración creada mientras el repositorio se está clonando más o menos.
Na13-c
3

Como han dicho otras personas, la razón por la que esto sucede es que el repositorio principal solo contiene una referencia a (el SHA1 de) una confirmación específica en el submódulo; no sabe nada acerca de las ramas. Así es como debería funcionar: la rama que estaba en esa confirmación puede haberse movido hacia adelante (o hacia atrás), y si el repositorio principal ha hecho referencia a la rama, entonces podría romperse fácilmente cuando eso suceda.

Sin embargo, especialmente si se está desarrollando activamente tanto en el repositorio principal como en el submódulo, el detached HEADestado puede ser confuso y potencialmente peligroso. Si realiza confirmaciones en el submódulo mientras está en detached HEADestado, estas se vuelven colgantes y puede perder fácilmente su trabajo. (Los commits colgantes generalmente se pueden rescatar usando git reflog, pero es mucho mejor evitarlos en primer lugar).

Si eres como yo, la mayoría de las veces si hay una rama en el submódulo que apunta a que se confirme la confirmación, preferirías verificar esa rama que estar en estado HEAD separado en la misma confirmación. Puede hacer esto agregando el siguiente alias a su gitconfigarchivo:

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

Ahora, después de hacerlo git submodule update, solo tiene que llamar git submodule-checkout-branch, y cualquier submódulo que se desproteja en una confirmación que tenga una rama apuntando a ella, desprotegerá esa rama. Si a menudo no tiene varias sucursales locales que apuntan a la misma confirmación, esto generalmente hará lo que desee; si no, entonces al menos se asegurará de que cualquier confirmación que realice vaya a una rama real en lugar de quedar colgando.

Además, si ha configurado git para actualizar automáticamente los submódulos al finalizar la compra (usando git config --global submodule.recurse true, vea esta respuesta ), puede hacer un enlace posterior a la finalización de la compra que llame a este alias automáticamente:

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

Entonces no necesita llamar a ninguno de los dos , git submodule updateo git submodule-checkout-branchsimplemente git checkoutactualizará todos los submódulos a sus respectivas confirmaciones y verificará las ramas correspondientes (si existen).

deltacrux
fuente
0

La solución más simple es:

git clone --recursive [email protected]:name/repo.git

Luego cd en el directorio repo y:

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

Lectura adicional: mejores prácticas de submódulos de Git .

noccoa0
fuente