Comprender la diferencia de ramificación entre SVN y Git

44

Soy un usuario de SVN y ahora estoy aprendiendo Git.

En SVN, generalmente pago en mi máquina local un repositorio, que incluye todas las ramas en mi proyecto y solía seleccionar la carpeta para mi rama que me interesa y trabajar allí.

Veo una diferencia usando Git.

Actualmente estoy clonando un repositorio y clono una rama específica usando gitk.

La carpeta del proyecto contiene solo el contenido de esa rama y no puedo ver todas las ramas como en SVN, lo cual es un poco confuso para mí.

No puedo encontrar una manera fácil de ver todas las ramas en mi repositorio local usando Git.

  • Me gustaría saber si el proceso de Git que describí es "estándar" y de alguna manera es correcto o me falta algo.

  • También me gustaría saber cómo manejar un proceso en el que necesito trabajar en dos ramas al mismo tiempo en caso de que, por ejemplo, necesite hacer una revisión en master pero mantener el contenido de otra rama también.

  • ¿Cuáles son las convenciones de nombre recomendadas para hacer las carpetas que incluyen la rama clonada del repositorio en Git, por ejemplo myproject-branchname?

Radex
fuente
8
Interesante: por lo general, con SVN solo verificaría el tronco o la rama en la que está trabajando, pero entiendo lo que quiere decir.
HorusKol
20
Había roto el flujo de trabajo en SVN, ahora intenta reflejarlo en Git (mientras que incluso el buen flujo de trabajo SVN se aplica solo parcialmente para Git). La mejor solución: olvida todos los hábitos de SVN y comienza en Git desde cero
Lazy Badger
66
git clonese parece más svnadmin hotcopyo svnrdump+ svnadmin loadde lo que es como svn checkout. Con git, usted no solicita los retazos de la repositorio; Usted copia todo el repositorio y trabaja con él localmente, empujando los cambios de regreso al repositorio "fuente" cuando y si lo desea. Git y Subversion usan dos modelos completamente diferentes para el control de fuente.
chepner
1
con respecto a la cosa de la revisión, considere usargit-flow
Bakuriu
55
@LazyBadger no se rompe el flujo de trabajo si facilita el desarrollo y proporciona valor. No hay una forma correcta de usar ramas.
dietbuddha

Respuestas:

67

Soy un usuario de SVN y ahora estoy aprendiendo GIT.

¡Bienvenido a la pandilla!

SVN Reeducación

En SVN generalmente [...]

Espera un momento. Si bien CVS y SVN y otros sistemas de control de versiones tradicionales (es decir, centralizados) cumplen (en su mayoría) el mismo propósito que los sistemas de control de versiones modernos (es decir, distribuidos) como mercurial y Git, será mucho mejor aprender Git desde cero en lugar de tratando de transferir su flujo de trabajo SVN a Git.

http://hginit.com ( ver en archive.org ) por Joel Spolsky (uno de los fundadores de Stack Exchange) es un tutorial para mercurial, no para Git, pero es el capítulo cero, "Subversion Re-education" es útil para usuarios que se han alejado de SVN para cualquier sistema de control de versiones distribuido, ya que le dice qué conceptos SVN tiene que (temporalmente) de la ONU -Aprender (o a stashdistancia , ya que los usuarios podrían decir Git) para ser capaz de envoltura de la cabeza alrededor del distribuida conceptos del sistema de control de versiones y los flujos de trabajo idiomáticos establecidos para trabajar con ellos.

Entonces puede leer ese capítulo cero y, en su mayoría, simplemente reemplazar la palabra "mercurial" por "Git" y así prepararse adecuadamente a sí mismo y a su mente para Git.

La letra pequeña

(Es posible omitir este por ahora.) Si bien voluble y Git son mucho más similares entre sí que a SVN, no son algunas diferencias conceptuales entre ellos, por lo que algunos de los estados y los consejos de "subversión reeducación" será técnicamente equivocada al reemplazar "mercurial" con "Git":

  • Mientras que mercurial rastrea y almacena internamente los conjuntos de cambios, Git rastrea y almacena internamente revisiones (es decir, estados del contenido de un árbol de directorios), al igual que lo hace Subversion. Pero aparte de Subversion, Git realiza fusiones al observar las diferencias entre cada rama involucrada, respectivamente, y una revisión de antepasado común (una verdadera fusión de 3 puntos), por lo que el resultado es muy similar al de mercurial: la fusión es mucho más fácil y menos error -propenso que en SVN.
  • Si bien puede ramificarse en Git clonando el repositorio (como es habitual en mercurial), es mucho más común crear una nueva ramificación dentro de un repositorio de Git. (Esto se debe a que las ramas de Git son simplemente punteros (en movimiento) a las revisiones, mientras que las ramas mercuriales son etiquetas permanentes aplicadas a cada revisión. Estas etiquetas permanentes generalmente no son deseadas, por lo que los flujos de trabajo mercuriales generalmente funcionan clonando el repositorio completo para un desarrollo divergente).

En SVN, todo es un directorio (pero no necesariamente debe tratarse como tal)

Pero te he estado interrumpiendo. ¿Estabas diciendo?

En SVN, generalmente pago en mi máquina local un repositorio, que incluye todas las ramas en mi proyecto y solía seleccionar la carpeta para mi rama que me interesa y trabajar allí.

Si, con eso, quiere decir que ha extraído la carpeta raíz del repositorio SVN (o cualquier otra carpeta que corresponda a más que trunka una sola rama, por ejemplo, en el diseño de repositorio SVN convencional, la branchescarpeta que contiene todas las ramas no troncales) entonces Me atrevo a decir que probablemente haya usado SVN incorrectamente (ish), o al menos haya abusado un poco del hecho de que el tronco, las ramas y las etiquetas están todas dobladas en un solo espacio de nombres (aunque jerárquico) junto con directorios dentro de las revisiones / base de código.

Si bien puede ser tentador cambiar varias ramas de SVN en paralelo, AFAIK no es así como se pretende usar SVN. (Aunque no estoy seguro de qué inconvenientes específicos podría tener un trabajo como ese).

En Git, solo los directorios son directorios

Cada clon de un repositorio de Git es en sí mismo un repositorio de Git: de forma predeterminada, obtiene una copia completa del historial del repositorio de origen, incluidas todas las revisiones, ramas y etiquetas. Pero mantendrá todo lo que tenga a su manera: Git lo almacena en una especie de base de datos basada en archivos, ubicada en la .git/subcarpeta oculta de la carpeta raíz del repositorio .

Pero, ¿cuáles son los archivos no ocultos que ve en la carpeta del repositorio?

Cuando eres git cloneun repositorio, Git hace varias cosas. Aproximadamente:

  1. Crea la carpeta de destino , y en ella, la .git/subcarpeta con la "base de datos"
  2. Transfiere las referencias (etiquetas, ramas, etc.) de la base de datos del repositorio de origen y hace una copia de ellas en la nueva base de datos, dándoles un nombre ligeramente modificado que las marca como referencias "remotas".
  3. Transfiere todas las revisiones (es decir, estados del árbol de archivos) a las que apuntan estas referencias, así como todas las revisiones a las que apuntan estas revisiones directa o transitivamente (sus padres y antepasados) a la nueva base de datos y las almacena, de modo que las nuevas referencias remotas realmente señale algo que esté disponible en el nuevo repositorio.
  4. Crea una rama local que rastrea la revisión remota correspondiente a la rama predeterminada del repositorio de origen (generalmente master).
  5. Comprueba esa sucursal local.

Ese último paso significa que Git busca la revisión a la que apunta esta rama y desempaqueta el árbol de archivos almacenado allí (la base de datos utiliza algunos medios de compresión y deduplicación) en la carpeta raíz del repositorio. Esa carpeta raíz y todas sus subcarpetas (excluyendo la .gitsubcarpeta especial ) se conocen como la "copia de trabajo" de su repositorio. Ahí es donde interactúa con el contenido de la revisión / rama actualmente desprotegida. Son solo carpetas y archivos normales. Sin embargo, para interactuar con el repositorio per-se (la "base de datos" de revisiones y referencias) utiliza gitcomandos.

Ver ramas de Git e interactuar con ramas de Git

Actualmente estoy clonando un repositorio y clono una rama específica usando gitk.

La versión de gitkI got no puede clonar repositorios. Solo puede ver el historial de repositorios y crear sucursales y consultar sucursales. Además, no hay "clonación" de una rama. Solo puede clonar repositorios.

¿Querías decir que clonaste un repositorio usando git clone ...y luego, usando gitk, revisas una rama?

La carpeta del proyecto contiene solo el contenido de esa rama y no puedo ver todas las ramas como en SVN, lo cual es un poco confuso para mí.

[...]

  • Me gustaría saber si el proceso git que describí es "estándar" y de alguna manera es correcto [...]

Sí, eso es bastante estándar:

  • Clonar un repositorio usando git clone ...
  • Echa un vistazo a la rama con la que quieres trabajar git checkout ...o utiliza una herramienta gráfica comogikt

No puedo encontrar una manera fácil de ver todas las ramas en mi repositorio local usando GIT.

  • [...] o me falta smt.

Tal vez:

  • puede enumerar sucursales locales con git branch
  • puede enumerar ramas remotas con git branch -ro todas las ramas congit branch -a
  • puede usar gitkpara ver el historial completo (todas las etiquetas de sucursales, etc., que su repositorio local conoce) invocando

    gitk --all
    

Cómo trabajar con múltiples ramas en paralelo

  • También me gustaría saber cómo manejar un proceso en el que necesito trabajar en dos ramas al mismo tiempo en caso de que, por ejemplo, necesite hacer una revisión en master pero mantener el contenido de otra rama también.

Hay diferentes escenarios aquí:

Un nuevo cambio (aún por crear) debe aplicarse a varias ramas

Use este flujo de trabajo:

  1. Cree una nueva rama a cpartir de una revisión que ya esté en la ascendencia de todas estas ramas (por ejemplo, la revisión que introdujo el error cuando el cambio será una corrección de error) o de una revisión que (incluyendo todos sus antepasados) es aceptable para ser introducida en Todas estas ramas.
  2. Realice y cometa el cambio en esa nueva rama c.
  3. Para cada rama bque necesita el cambio:

    1. Echa un vistazo a b:

      git checkout b
      
    2. Fusionarse cen b:

      git merge c
      
  4. Eliminar rama c:

    git branch --delete c
    

Se necesita un cambio existente en una sucursal diferente

(... pero sin los otros cambios realizados en el lugar donde reside ese cambio)

  1. Echa un vistazo a la sucursal donde se necesita el cambio
  2. Seleccione las revisiones que realizan el cambio, en orden

En una rama a, desea cambiar uno o algunos archivos al estado exacto que tienen en una rama diferenteb

  1. Revisa a
  2. Obtenga el contenido del archivo de la rama b:

    git checkout b -- path/to/a/file.txt path/to/another/file.cpp or/even/a/complete/directory/ ...
    

    (Además de git checkoutsin pasar rutas, esto no cambiará a rama b, solo obtendrá el contenido del archivo solicitado desde allí. Estos archivos pueden o no existir ya a. Si lo hacen, se sobrescribirán con su contenido activado b).

Mientras trabaja en una rama, desea ver cómo están las cosas en otra rama

Echa un vistazo a la rama en la que quieres trabajar.

Luego, por mirar la otra rama,

  • use herramientas gráficas que le permitan ver el contenido de las revisiones que actualmente no están desprotegidas (por ejemplo, al gitkintentar cambiar los botones de radio de "parche" a "árbol")
  • o clonar el repositorio en un directorio temporal y verifique la otra rama allí
  • o use git worktreepara crear un directorio de trabajo separado del mismo repositorio (es decir, también usando la base de datos en el .git/directorio de su repositorio local actual) donde puede verificar esa otra rama
das-g
fuente
Para el último flujo de trabajo, stashes ideal.
CAD97
@ CAD97 no si desea continuar trabajando en la primera rama, mientras mira la segunda como referencia en paralelo . git stashEs bueno quitar el trabajo inacabado, por ejemplo, para cambiar de rama y luego volver.
das-g
1
"los flujos de trabajo mercuriales generalmente funcionan clonando el repositorio completo para un desarrollo divergente" - Eh, he oído hablar de esto, pero la mayoría de las personas que quieren ramas Git solo usan marcadores en lugar de clonar todo el repositorio. Es mucho más fácil.
Kevin
@ Kevin Eso podría ser. Hace algún tiempo utilicé mercurial por última vez, así que tal vez era diferente en ese entonces. (O tal vez fueron solo los flujos de trabajo de la comunidad con la que lo usé que eran así.)
das-g
Quizás el texto Hg Init "[...]" porque realmente debería haber estado ramificando la forma Mercurial, clonando repositorios "[...] también debería actualizarse en ese sentido.
das-g
31

En SVN, generalmente pago en mi máquina local un repositorio, que incluye todas las ramas en mi proyecto y solía seleccionar la carpeta para mi rama que me interesa y trabajar allí.

A menos que revise el directorio sobre el enlace troncal, en Subversion generalmente no tiene todas las ramas disponibles localmente. En Git, todo el contenido (confirmaciones, mensajes, ramas, etc.) siempre se clona en cada copia.

Actualmente estoy clonando un repositorio y clono una rama específica usando gitk.

No es posible, ver arriba. Puedes git checkout branch-name, pero no puedes git clone something branch-name. En Subversion, las ramas son carpetas. En Git, las ramas son punteros a un commit.

No puedo encontrar una manera fácil de ver todas las ramas en mi repositorio local usando GIT.

Ejecutar git branch. Por defecto, solo muestra sucursales locales. Use git branch --remotepara ver ramas remotas.

l0b0
fuente
44
Hm. "Por defecto, solo muestra sucursales locales". Parece que hay otros tipos de ramas que no son locales. Pero también dice "En Git todo el contenido (confirmaciones, mensajes, ramas, etc.) siempre se clona en cada copia". . Me parece un poco confuso, porque si todas las ramas se clonan en su copia, ¿cuáles son esas ramas misteriosas no locales? Tal vez sea demasiado complicado responder en un campo de comentarios.
tubería
2
@pipe Cuando confirma los cambios, lo hace creando una confirmación local, que cambia la confirmación a la que apunta la rama. Si desea que otros obtengan estos cambios, debe enviar estos cambios al repositorio remoto. Esto hace que la rama remota apunte a esta nueva confirmación. Ahora todos los demás pueden extraer estos cambios desde allí y, como resultado, todos eventualmente obtendrían una copia completa del repositorio
Frozn
99
@pipe Primero, de hecho, puede clonar solo una rama usando --single-branch(incluso solo la punta con --depth 1). La diferencia entre las ramas locales y remotas conocidas por git es simplemente un tipo de etiqueta. Las ramas remotas tienen el prefijo del nombre de la fuente remota (a menudo origin). Las sucursales locales no tienen ese prefijo. Pero al final, los datos están disponibles localmente (a menos que lo haya hecho --single-brancho algo similar). Cuando se ejecuta git checkout $branchnamecon una rama para la que no existe una rama local sino remota, git configura automáticamente una rama local que "rastrea" el control remoto.
Jonas Schäfer
"A menos que revise el directorio sobre el tronco" - Mi experiencia es que esto es bastante común, ya que hace que la fusión sea mucho más fácil. (Aunque usamos una única rama "en vivo" en lugar de etiquetas de versión, por lo general solo equivale a 2-3 copias del código)
Izkata
1
@Izkata "hace que la fusión sea mucho más fácil", ¿verdad?
HorusKol
3

Como esto está etiquetado con , espero que mi falta de conocimiento de SVN sea descuidada.

Actualmente estoy clonando un repositorio y clono una rama específica usando gitk.

Está clonando todo el repositorio remoto, no solo una rama específica.

El repositorio se imagina mejor como una base de datos, por lo que está haciendo un clon del estado actual de la base de datos remota. Pero después de eso, está trabajando en su propia copia de esa base de datos; si se compromete, modifica su base de datos local.

El fetchcomando se utiliza para mantener la base de datos local sincronizada con la remota.

Por lo general, desde esa base de datos local, revisa una sucursal para trabajar. Esto no es más que un marcador interno para git, donde comenzó su trabajo actual.

Digamos que estás trabajando en un repositorio simple, donde no hay una rama además master, puedes echar un vistazo a la .gitcarpeta para desvelar la "magia":

Suponga que su último commit (on master) fue 182e8220b404437b9e43eb78149d31af79040c66, encontrará exactamente eso debajo cat .git/refs/heads/master.

A partir de eso, ramifica una nueva rama git checkout -b mybranch, encontrará exactamente el mismo puntero en el archivo cat .git/refs/heads/mybranch.

Las ramas no son más que "punteros". El marcador de "trabajo" se llama a HEAD.

Si desea saber dónde está su HEADestá en:

cat .git/HEADque dice ref: refs/heads/mybranch, por ejemplo , que a su vez apunta ( cat .git/refs/heads/mybranch) a un hash de confirmación78a8a6eb6f82eae21b156b68d553dd143c6d3e6f

Las confirmaciones reales se almacenan en la objectscarpeta (el cómo es un tema propio).

La carpeta del proyecto contiene solo el contenido de esa rama y no puedo ver todas las ramas como en SVN, lo cual es un poco confuso para mí.

No confunda el working directorycon la "base de datos git" en su conjunto. Como dije anteriormente, su directorio de trabajo es solo una instantánea de (quizás) un subconjunto.

Digamos que tiene diferentes sucursales, su directorio de trabajo está dedicado a trabajar solo en esa rama (aunque podría colocar el trabajo desde allí en otro lugar).

Por lo general, si desea ver qué ramas están definidas para el proyecto, tiene la posibilidad de

  • git branch para sucursales locales
  • git branch --remote para sucursales remotas
  • git branch -a para ambos

(o git branch -v)

Dado que git es un sistema de control de versiones distribuido, no solo es posible, sino que se recomienda, hacer diferentes ramas localmente / remotas.

Mi flujo de trabajo típico es:

  • bifurcar una ramificación característica
  • bifurcar una rama WIP (trabajo en progreso) de ese
  • trabaje como quiera, incluso si se compromete después de una sola línea; No importa

Cuando se completa la función:

  • aplastar / reelaborar la WIPrama (con rebase interactivo) = hacer un solo commit desde ese
  • fusionar la WIPrama en la rama de características y ofrecer que (si trabaja con github esa oferta se llamaría una "solicitud de extracción") para integrarse en la rama estable (maestra).

También me gustaría saber cómo manejar un proceso en el que necesito wprl en dos ramas al mismo tiempo en caso de que, por ejemplo, necesite hacer una revisión en master pero mantener el contenido de otra rama también.

Depende de cómo esté estructurado su proyecto:

Digamos que tienes un maestro estable. Y las características solo se desarrollan a partir de esa rama estable, por lo que generalmente está detrás de una rama de características. Entonces tendría un último commit en master que sería la raíz de la rama de características.

Luego, realizaría una confirmación en la rama maestra y podría decidir si fusionar ambas ramas o volver a crear una base (lo cual es una especie de fusión para usuarios con necesidades avanzadas, por así decirlo).

O siempre puede realizar cambios en las ramas (p master. Ej. ) Y seleccionarlos en otras ramas.

¿Qué es un convenio de nombre recomendado para hacer que las carpetas que incluyen la rama clonada desde el repositorio en GIT, por ejemplo myproject-branchname

Es tu decision.

Por lo general, terminas con el nombre de los repositorios.

Pero hay ocasiones, cuando esto no se quiere:

por ejemplo, clonas oh-my-zsh con git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh Aquí .oh-my-zshse nombra explícitamente como el objetivo.

Thomas Junk
fuente
2

Git clone en realidad clona todo el repositorio, pero establece su directorio de trabajo en el valor predeterminado (generalmente maestro).

Puede ver otras ramas usando git branchpara ver ramas locales o git branch -rremotas y luego git checkoutcambiar a una rama existente o crear una nueva basada en la rama actual.

Debería leer la documentación de git para obtener más detalles, y Atlassian también tiene algunos buenos artículos al respecto.

HorusKol
fuente
0

Las ramas en git y svn son cosas fundamentalmente diferentes.

En svn, una rama (o una etiqueta) es un directorio en el repositorio.

En git, una rama (o una etiqueta) es un puntero a una confirmación.

Con svn puedes hacerlo si deseas pagar la raíz del repositorio. Esto significa que tiene todas las ramas y etiquetas desprotegidas a la vez. De hecho, esta no es la forma normal de usar svn.

Una rama git no es un directorio, por lo que no existe el privilegio de "verificar la raíz del repositorio". Existe cierto soporte para múltiples árboles de trabajo, por lo que supongo que podría improvisar un script para verificar cada rama al mismo tiempo si realmente quisiera, pero sería un pensamiento bastante inusual.

Además, con SVN hay exactamente un repositorio. Con git cada desarrollador tiene su propio repositorio. Esto significa que los desarrolladores pueden trabajar sin conexión, pero también significa que diferentes desarrolladores pueden tener una idea diferente de lo que hay en cada rama.

En virtud de ser directorios y en virtud del modelo de historia lineal simple de svn, las ramas de svn tienen historias sólidas. Si desea saber qué había en la rama x en la fecha y, puede hacer esa pregunta fácilmente.

Las ramas de Git, por otro lado, realmente no tienen historias. Existe el "reflog", pero está pensado más como un mecanismo de recuperación ante desastres que como un historial a largo plazo. En particular, el reflog no se puede leer de forma remota y está deshabilitado de forma predeterminada para repositorios desnudos.

Los commits, por supuesto, tienen historias, pero esas historias no son suficientes para responder a la pregunta de "qué estaba en la rama x del repositorio en la fecha z".

No puedo encontrar una manera fácil de ver todas las ramas en mi repositorio local usando Git.

Puede enumerar todas las sucursales locales escribiendo "git branch".

Puede enumerar todas las ramas locales y remotas utilizando "git branch -a"

También me gustaría saber cómo manejar un proceso en el que necesito trabajar en dos ramas al mismo tiempo en caso de que, por ejemplo, necesite hacer una revisión en master pero mantener el contenido de otra rama también.

Un par de opciones

Puede confirmar sus cambios en la "otra rama", cambiar a maestro, hacer su trabajo allí y luego volver a cambiar.

También puede crear un árbol de trabajo adicional. Google "git-worktree" para los detalles sobre la sintaxis.

¿Cuáles son las convenciones de nombre recomendadas para hacer que las carpetas que incluyen la rama se clonen desde el repositorio en Git, por ejemplo, myproject-branchname?

No creo que haya una convención establecida. Tener varios árboles de trabajo desprotegidos a la vez es la excepción, no la regla.

Peter Green
fuente
2
esta respuesta parece incompleta, ¿por qué?
mosquito