¿Cómo puedo especificar una rama / etiqueta al agregar un submódulo Git?

756

Como git submodule add -bfunciona

Después de agregar un submódulo con una rama específica, un nuevo repositorio clonado (después git submodule update --init) estará en una confirmación específica, no la rama en sí ( git statusen el submódulo muestra "Actualmente no en ninguna rama").

No puedo encontrar ninguna información sobre .gitmoduleso .git/configsobre la rama del submódulo o cualquier confirmación específica, entonces, ¿cómo lo resuelve Git?

Además, ¿es posible especificar una etiqueta en lugar de una rama?

Estoy usando la versión 1.6.5.2.

Ivan
fuente
3
Si tiene un submódulo existente que aún no rastrea una rama , pero desea que ahora
rastree

Respuestas:

745

Nota: Git 1.8.2 agregó la posibilidad de rastrear ramas. Vea algunas de las respuestas a continuación.


Es un poco confuso acostumbrarse a esto, pero los submódulos no están en una rama. Son, como usted dice, solo un puntero a una confirmación particular del repositorio del submódulo.

Esto significa que, cuando alguien más revisa su repositorio o extrae su código y actualiza git submodule, el submódulo se desprotege para ese commit en particular.

Esto es excelente para un submódulo que no cambia a menudo, porque entonces todos en el proyecto pueden tener el submódulo en el mismo commit.

Si desea mover el submódulo a una etiqueta en particular:

cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push

Luego, otro desarrollador que quiere cambiar el submodule_directory a esa etiqueta, hace esto

git pull
git submodule update --init

git pullcambios que comprometen sus puntos de directorio de submódulos. git submodule updateen realidad se fusiona en el nuevo código.

djacobs7
fuente
8
Esa es una muy buena explicación, gracias! Y, por supuesto, después de leer su respuesta, me di cuenta de que la confirmación se guarda dentro del submódulo (submodule / .git / HEAD).
Ivan
44
Esto no parece funcionar en git 1.7.4.4. cd my_submodule; git checkout [ref in submodule's repositoryrendimientos fatal: reference is not a tree: .... Es como si gitsolo operara en el repositorio principal.
James A. Rosen
3
Es bueno usar submódulos git incluso para proyectos que se actualizan con frecuencia. El núcleo de Linux lo utiliza y no es tan malo
10
¿Es git checkout v1.0una rama o una etiqueta?
Bernhard Döbler
8
Considere una etiqueta como un alias legible por humanos para un commit. Y un commit es un conjunto de estados específicos para cada archivo. Una rama es esencialmente lo mismo, excepto que puede hacer cambios en ella.
deadbabykitten
657

Me gustaría agregar una respuesta aquí que en realidad es solo un conglomerado de otras respuestas, pero creo que puede ser más completa.

Sabes que tienes un submódulo Git cuando tienes estas dos cosas.

  1. Tu .gitmodulestiene una entrada como esta:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
    
  2. Tiene un objeto de submódulo (denominado SubmoduleTestRepo en este ejemplo) en su repositorio de Git. GitHub los muestra como objetos "submódulos". O hacerlo git submodule statusdesde una línea de comando. Los objetos de submódulo Git son tipos especiales de objetos Git y contienen la información SHA para una confirmación específica.

    Cada vez que haga un git submodule update, rellenará su submódulo con contenido del commit. Sabe dónde encontrar el commit debido a la información en el .gitmodules.

    Ahora, todo lo que -bhace es agregar una línea en su .gitmodulesarchivo. Entonces, siguiendo el mismo ejemplo, se vería así:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
        branch = master
    

    Nota: solo se admite el nombre de la sucursal en un .gitmodulesarchivo, ¡pero SHA y TAG no son compatibles! (en lugar de eso, la confirmación de la rama de cada módulo se puede rastrear y actualizar usando " git add .", por ejemplo git add ./SubmoduleTestRepo, como , y no es necesario cambiar el .gitmodulesarchivo cada vez)

    El objeto submódulo todavía apunta a una confirmación específica. Lo único que la -bopción te compra es la posibilidad de agregar una --remotebandera a tu actualización según la respuesta de Vogella:

    git submodule update --remote
    

    En lugar de completar el contenido del submódulo en el commit señalado por el submódulo, reemplaza ese commit con el último commit en la rama maestra, LUEGO llena el submódulo con ese commit. Esto se puede hacer en dos pasos por respuesta djacobs7. Como ahora ha actualizado la confirmación a la que apunta el objeto de submódulo, debe confirmar el objeto de submódulo modificado en su repositorio de Git.

    git submodule add -bNo es una forma mágica de mantener todo actualizado con una rama. Simplemente agrega información sobre una rama en el .gitmodulesarchivo y le da la opción de actualizar el objeto de submódulo a la última confirmación de una rama específica antes de completarla.

Johnny Z
fuente
14
Esta respuesta debería tener más votos positivos. He estado leyendo muchas publicaciones durante el último día y esto aclara toda la confusión. Viniendo del mundo SVN y usando elementos externos, uno quiere creer que el seguimiento de rama de submódulo git mantiene mágicamente todo actualizado desde la rama, ¡pero esto no es cierto! ¡Tienes que actualizarlos explícitamente! Como mencionas, debes confirmar los objetos submódulos modificados.
dtmland
12
¿Este seguimiento de rama también funciona con etiquetas ? En lugar de una rama, especifiqué una etiqueta en mi .gitmodulesy después de hacerlo $ git submodule update --init --remote TestModulerecibí un error que decía fatal: Needed a single revisiony Unable to find current origin/TestTag revision in submodule path 'TestModule'. Al hacerlo con una rama real, funciona. ¿Hay alguna forma de especificar una etiqueta .gitmodulessin tener que especificar la confirmación exacta?
Hhut
55
Esto no parece funcionar. ¿Actualicé el hash .gitmodulesy corrí git submodule updatey no pasó nada?
CMCDragonkai
2
De alguna manera esto no funciona para mí. Con un SHA Commit Identificación, siempre me da un error "No se puede encontrar la versión actual (I verificó dos veces el número de revisión de la cabeza y su correcta) Sin embargo si uso principal funciona..
infoclogged
2
Ingresar un SHA en el atributo de rama tampoco funciona para mí. Este uso tampoco es compatible con los documentos: git-scm.com/docs/gitmodules
Jakub Bochenski
340

(Git 2.22, Q2 2019, ha introducido git submodule set-branch --branch aBranch -- <submodule_path>)

Tenga en cuenta que si tiene un submódulo existente que aún no está rastreando una rama , entonces ( si tiene git 1.8.2+ ):

  • Asegúrese de que el repositorio principal sepa que su submódulo ahora rastrea una rama:

    cd /path/to/your/parent/repo
    git config -f .gitmodules submodule.<path>.branch <branch>
    
  • Asegúrese de que su submódulo sea realmente el último de esa rama:

    cd path/to/your/submodule
    git checkout -b branch --track origin/branch
      # if the master branch already exist:
      git branch -u origin/master master
    

         (con 'origen' es el nombre de la distancia aguas arriba de recompra del submódulo se ha clonado a partir de.
         Un git remote -vdentro de esa sub-módulo mostrará la misma. Por lo general, es 'origen')

  • No olvide registrar el nuevo estado de su submódulo en su repositorio principal:

    cd /path/to/your/parent/repo
    git add path/to/your/submodule
    git commit -m "Make submodule tracking a branch"
    
  • La actualización posterior para ese submódulo tendrá que usar la --remoteopción:

    # update your submodule
    # --remote will also fetch and ensure that
    # the latest commit from the branch is used
    git submodule update --remote
    
    # to avoid fetching use
    git submodule update --remote --no-fetch 
    

Tenga en cuenta que con Git 2.10+ (Q3 2016), puede usar ' .' como nombre de sucursal:

El nombre de la rama se registra como submodule.<name>.branchen .gitmodulesfor update --remote.
Se .utiliza un valor especial de para indicar que el nombre de la rama en el submódulo debe ser el mismo nombre que la rama actual en el repositorio actual .

Pero, como se ha comentado por LubosD

Con git checkout, si el nombre de la rama a seguir es " .", ¡matará su trabajo no comprometido!
Usar en su git switchlugar.

Eso significa Git 2.23 (agosto de 2019) o más.

Ver " Confundido porgit checkout "


Si desea actualizar todos sus submódulos siguiendo una rama:

    git submodule update --recursive --remote

Tenga en cuenta que el resultado, para cada submódulo actualizado, casi siempre será un HEAD separado , como lo nota Dan Cameron en su respuesta .

( Clintm señala en los comentarios que, si ejecuta git submodule update --remotey el sha1 resultante es el mismo que la rama en la que se encuentra actualmente el submódulo, no hará nada y dejará el submódulo todavía "en esa rama" y no en el estado de la cabeza separada. )

Para asegurarse de que la rama esté realmente desprotegida (y eso no modificará el SHA1 de la entrada especial que representa el submódulo para el repositorio principal), sugiere:

git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git switch $branch'

Cada submódulo seguirá haciendo referencia al mismo SHA1, pero si realiza nuevas confirmaciones, podrá empujarlas porque serán referenciadas por la rama a la que desea que el submódulo rastree.
Después de ese empuje dentro de un submódulo, no olvide volver al repositorio principal, agregar, confirmar y empujar el nuevo SHA1 para esos submódulos modificados.

Tenga en cuenta el uso de $toplevel, recomendado en los comentarios de Alexander Pogrebnyak .
$toplevelse introdujo en git1.7.2 en mayo de 2010: commit f030c96 .

contiene la ruta absoluta del directorio de nivel superior (donde .gitmodulesestá).

dtmlandagrega en los comentarios :

La secuencia de comandos foreach no podrá extraer submódulos que no siguen una rama.
Sin embargo, este comando te da ambos:

 git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git switch $branch' –

El mismo comando pero más fácil de leer:

git submodule foreach -q --recursive \
    'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; \
     [ "$branch" = "" ] && \
     git checkout master || git switch $branch' –

umläute refina el comando de dtmland con una versión simplificada en los comentarios :

git submodule foreach -q --recursive 'git switch $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

líneas múltiples:

git submodule foreach -q --recursive \
  'git switch \
  $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

Antes de Git 2.26 (Q1 2020), una búsqueda a la que se le pide que busque actualizaciones de forma recurrente en submódulos inevitablemente produce resmas de salida, y se hace difícil detectar mensajes de error.

Se le ha enseñado al comando a enumerar submódulos que tenían errores al final de la operación .

Ver commit 0222540 (16 de enero de 2020) por Emily Shaffer ( nasamuffin) .
(Fusionada por Junio ​​C Hamano - gitster- en commit b5c71cc , 05 de febrero de 2020)

fetch: enfatizar la falla durante la recuperación del submódulo

Firmado por: Emily Shaffer

En los casos en que una recuperación de submódulos falla cuando hay muchos submódulos, el error de la recuperación de submódulos que falla sola queda enterrado bajo actividad en los otros submódulos si más de una recuperación se recupera fetch-by-oid.
Llame una falla tarde para que el usuario sepa que algo salió mal y dónde .

Porque fetch_finish()solo se llama sincrónicamente mediante run_processes_parallel,mutexing no se requiere alrededor submodules_with_errors.

VonC
fuente
1
Pregunta: si tengo la carpeta subModule1 y deseo rastrear la rama maestra, el comando resultante se vería así: git config -f .gitmodules submodule.subModule1.branch master
BraveNewMath
1
La foreachsecuencia de comandos no dependerá del código rígido <path>, si lo sustituye <path>con $toplevel/.
Alexander Pogrebnyak
1
La foreachsecuencia de comandos no podrá extraer submódulos que no siguen una rama. Sin embargo, este comando te da ambos:git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git checkout $branch'
dtmland
2
Aquí hay una versión simplificada del script de @ dtmland:git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
2015
1
Ohh! En realidad, el guión foreach es innecesario. Tenemos que ejecutar la actualización del submódulo con el interruptor --merge o --rebase: git submodule update --remote --mergeo git submodule update --remote --rebase. Estos comandos hacen el seguimiento de la rama remota.
GregTom
206

Git 1.8.2 agregó la posibilidad de rastrear ramas.

# add submodule to track master branch
git submodule add -b branch_name URL_to_Git_repo optional_directory_rename

# update your submodule
git submodule update --remote 

Ver también submódulos Git

vogella
fuente
44
¿Esto se aplica también a las etiquetas?
ThorSummoner
1
¿Cómo se agrega al submódulo de esa manera reflejado en el .gitmodulesarchivo?
Eugene
1
Gracias, acabo de utilizar la información sobre para ayudarme a crear una carpeta de submódulos que se sincroniza con un sitio web de gitHub
Dinis Cruz
44
Puede bloquear una etiqueta con la git submodule add -b tags/<sometag> <url>que puede ver como la línea branch = tags/<sometag>en.gitmodules
KCD
99
@KCD ¿Qué versión de git puede hacer eso con etiquetas? ¿El mío no funciona?
CMCDragonkai
58

Un ejemplo de cómo uso los submódulos de Git.

  1. Crea un nuevo repositorio
  2. Luego clone otro repositorio como un submódulo
  3. Luego tenemos ese submódulo que usa una etiqueta llamada V3.1.2
  4. Y luego nos comprometemos.

Y eso se parece un poco a esto:

git init 
vi README
git add README
git commit 
git submodule add git://github.com/XXXXX/xxx.yyyy.git stm32_std_lib
git status

git submodule init
git submodule update

cd stm32_std_lib/
git reset --hard V3.1.2 
cd ..
git commit -a

git submodule status 

¿Tal vez ayuda (aunque uso una etiqueta y no una rama)?

Johan
fuente
44
Básicamente es la misma respuesta que djacobs7, pero gracias de todos modos :)
Ivan
1
¿Deberías poder realizar un cambio después de tu git reset --hard V3.1.2? Acabo de recibir un "nada que confirmar" con uno git statusdel directorio principal.
Nick Radford
1
@Ivan: ¿Podría explicar cómo esto es lo mismo que la respuesta de djacobs7? Por lo que veo, su respuesta ni siquiera incluye el comando 'agregar submódulo', sino que el repositorio se agrega directamente, sin ningún enlace al repositorio git original del módulo. Al menos cuando probé este enfoque no había ningún enlace en .gitmodules.
Michel Müller
La respuesta de djacobs7 no incluye toda la explicación a partir de agregar el submódulo. Asume que ya lo tienes.
CodeMonkey
¿No agrega simplemente todo el contenido del submódulo como objetos rastreados a su repositorio principal?
user1312695
38

En mi experiencia, el cambio de sucursales en el superproyecto o los pagos futuros seguirá causando HEADs separados de submódulos, independientemente de si el submódulo se ha agregado y rastreado correctamente (es decir, @ djacobs7 y @Johnny Z respuestas).

Y en lugar de verificar manualmente la rama correcta manualmente o mediante un script, se puede usar git submodule foreach .

Esto verificará el archivo de configuración del submódulo para la propiedad de la rama y revisará la rama establecida.

git submodule foreach -q --recursive 'branch="$(git config -f <path>.gitmodules submodule.$name.branch)"; git checkout $branch'

Dan Cameron
fuente
Agradable. +1. He incluido su comando en mi respuesta .
VonC
33

Los submódulos de Git son un poco extraños, siempre están en modo de "cabeza separada", no se actualizan a la última confirmación en una rama como es de esperar.

Sin embargo, esto tiene sentido cuando lo piensas. Digamos que creo un repositorio foo con la barra de submódulos . Empujo mis cambios y le digo que compruebe commit a7402be desde el repositorio foo .

Luego imagina que alguien comete un cambio en la barra del repositorio antes de que puedas hacer tu clon.

Cuando eche un vistazo, confirme a7402be desde el repositorio foo , espera obtener el mismo código que introduje. Es por eso que los submódulos no se actualizan hasta que les dices explícitamente y luego realizas una nueva confirmación.

Personalmente, creo que los submódulos son la parte más confusa de Git. Hay muchos lugares que pueden explicar los submódulos mejor que yo. Recomiendo Pro Git de Scott Chacon.

Neall
fuente
Creo que es hora de que empiece a leer algunos libros de git, gracias por la recomendación.
Ivan
Lo sentimos, pero no aclaró si uno obtendría lo mismo que empujó a a7402be, u obtendrá lo último de bar, a través de su versión de foo. Gracias :)
mmm
66
El problema es que debería haber una opción para decir "mantener este submódulo en la rama X", de modo que si DESEA que se actualice automáticamente, puede hacer que eso suceda. Haría que los submódulos fueran mucho más útiles para administrar, por ejemplo, una instalación de WordPress donde los complementos son repositorios de Git sin tener que volver a guardar el superproyecto para cada complemento que se actualice.
jerclarke
@jeremyclark git clone git://github.com/git/git.gity presionar esa función ...? = D
Alastair el
1
La parte más confusa sobre Git es que incluso después de más de una década de desarrollo, una herramienta que está destinada a ayudarme a hacer mi trabajo todavía tiene una experiencia de usuario tan mala y, por razones completamente ajenas a mí, a la gente le gusta que Git le muestre el dedo. el tiempo.
0xC0000022L
20

Para cambiar la rama de un submódulo (suponiendo que ya tenga el submódulo como parte del repositorio):

  • cd a la raíz de su repositorio que contiene los submódulos
  • Abierto .gitmodulespara editar
  • Agregue la línea a continuación path = ...y url = ...eso dice branch = your-branch, para cada submódulo; guardar el archivo .gitmodules.
  • entonces sin cambiar el directorio hacer $ git submodule update --remote

... esto debería extraer los últimos commits en la rama especificada, para cada submódulo así modificado.

Ingeniero
fuente
10

Tengo esto en mi archivo .gitconfig. Todavía es un borrador, pero resultó útil a partir de ahora. Me ayuda a volver a conectar siempre los submódulos a su rama.

[alias]

######################
#
#Submodules aliases
#
######################


#git sm-trackbranch : places all submodules on their respective branch specified in .gitmodules
#This works if submodules are configured to track a branch, i.e if .gitmodules looks like :
#[submodule "my-submodule"]
#   path = my-submodule
#   url = [email protected]/my-submodule.git
#   branch = my-branch
sm-trackbranch = "! git submodule foreach -q --recursive 'branch=\"$(git config -f $toplevel/.gitmodules submodule.$name.branch)\"; git checkout $branch'"

#sm-pullrebase :
# - pull --rebase on the master repo
# - sm-trackbranch on every submodule
# - pull --rebase on each submodule
#
# Important note :
#- have a clean master repo and subrepos before doing this !
#- this is *not* equivalent to getting the last committed 
#  master repo + its submodules: if some submodules are tracking branches 
#  that have evolved since the last commit in the master repo,
#  they will be using those more recent commits !
#
#  (Note : On the contrary, git submodule update will stick 
#to the last committed SHA1 in the master repo)
#
sm-pullrebase = "! git pull --rebase; git submodule update; git sm-trackbranch ; git submodule foreach 'git pull --rebase' "

# git sm-diff will diff the master repo *and* its submodules
sm-diff = "! git diff && git submodule foreach 'git diff' "

#git sm-push will ask to push also submodules
sm-push = push --recurse-submodules=on-demand

#git alias : list all aliases
#useful in order to learn git syntax
alias = "!git config -l | grep alias | cut -c 7-"
Pascal T.
fuente
3

Usamos Quack para extraer un módulo específico de otro repositorio de Git. Necesitamos extraer el código sin toda la base de código del repositorio proporcionado; necesitamos un módulo / archivo muy específico de ese enorme repositorio y debemos actualizarlo cada vez que ejecutamos la actualización.

Entonces lo logramos de esta manera:

Crear configuración

name: Project Name

modules:
  local/path:
    repository: https://github.com/<username>/<repo>.git
    path: repo/path
    branch: dev
  other/local/path/filename.txt:
    repository: https://github.com/<username>/<repo>.git
    hexsha: 9e3e9642cfea36f4ae216d27df100134920143b9
    path: repo/path/filename.txt

profiles:
  init:
    tasks: ['modules']

Con la configuración anterior, crea un directorio desde el repositorio GitHub proporcionado como se especifica en la configuración del primer módulo, y el otro es extraer y crear un archivo desde el repositorio dado.

Otros desarrolladores solo necesitan ejecutar

$ quack

Y extrae el código de las configuraciones anteriores.

Love Sharma
fuente
2

El único efecto de elegir una rama para un submódulo es que, cada vez que pasa la --remoteopción en la git submodule updatelínea de comando, Git revisará en modo HEAD separado (si --checkoutse selecciona el comportamiento predeterminado ) la última confirmación de esa rama remota seleccionada .

Debe tener especial cuidado al usar esta función de seguimiento de rama remota para submódulos Git si trabaja con clones poco profundos de submódulos. La rama que elija para este propósito en la configuración de submódulos NO ES la que se clonará durante git submodule update --remote. Si pasa también el --depthparámetro y no le indica a Git sobre qué rama desea clonar , ¡ y en realidad no puede en la git submodule updatelínea de comando! -, se comportará implícitamente como se explica en la git-clone(1)documentación para git clone --single-branchcuando --branchfalta el parámetro explícito y, por lo tanto , clonará solo la rama primaria .

Sin sorpresa, después de la etapa de clonación realizada por el git submodule updatecomando, finalmente intentará verificar la última confirmación para la rama remota que configuró previamente para el submódulo y, si esta no es la principal, no es parte de su clon superficial local, y por lo tanto fallará con

fatal: necesitaba una sola revisión

No se puede encontrar la revisión del origen actual / NotThePrimaryBranch en la ruta del submódulo 'mySubmodule'

LuKePicci
fuente
¿Cómo solucionar el error? ¿Necesita una sola revisión?
NidhinSPradeep