Rebasar una sucursal que sea pública.

6

Estoy fallando en entender como usar git-rebase, y considero el siguiente ejemplo.

Vamos a empezar un repositorio en ~/tmp/repo:

$ git init

Luego agrega un archivo foo

$ echo "hello world" > foo

que luego se agrega y se compromete:

$ git add foo
$ git commit -m "Added foo"

A continuación, comencé un repositorio remoto. En ~/tmp/bare.git corrí

$ git init --bare

Para enlazar repo a bare.git corrí

$ git remote add origin ../bare.git/
$ git push --set-upstream origin master

A continuación, vamos a la rama, agregue un archivo y establezca un flujo ascendente para la nueva rama b1:

$ git checkout -b b1
$ echo "bar" > foo2
$ git add foo2
$ git commit -m "add foo2 in b1"
$ git push --set-upstream origin b1

Ahora es el momento de volver a master y cambiar algo allí:

$ echo "change foo" > foo
$ git commit -a -m "changed foo in master"
$ git push

En este punto en master el archivo foo Contiene cambiado foo , mientras en b1 aún es Hola Mundo . Finalmente, quiero sincronizar. b1 con el progreso realizado en master.

$ git checkout b1
$ git fetch origin
$ git rebase origin/master

En este punto git st devoluciones:

# On branch b1
# Your branch and 'origin/b1' have diverged,
# and have 2 and 1 different commit each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean

En este punto el contenido de foo en la rama b1 es cambiar foo también. Entonces, ¿qué significa esta advertencia? Esperaba que hiciera una git push, Git sugiere hacer git pull... De acuerdo a esta respuesta , esto es más o menos eso, y en su comentario @FrerichRaabe dice explícitamente que no necesito hacer un tirón. ¿Que está pasando aqui? ¿Cuál es el peligro, cómo se debe proceder? ¿Cómo debe mantenerse la historia consistente? ¿Cuál es la interacción entre el caso descrito anteriormente y la siguiente cita:

No vuelva a marcar las confirmaciones que haya enviado a un repositorio público.

tomado de libro pro git .

Supongo que está relacionado de alguna manera, y si no me encantaría saber por qué. ¿Cuál es la relación entre el escenario anterior y el procedimiento que describí? en este post .

Dror
fuente
La razón git st Da esa salida porque Git sabe que tu local. b1 rama está siguiendo origin/b1, así que eso es lo que quieres reajustar. Tu corres git rebase origin/master Sin embargo, por lo que ha vuelto a basar ("repetido") su b1 cometer encima de origin/master.
Frerich Raabe
@FrerichRaabe: Déjame intentar y reformular. Tu dices eso asumiendo origin/master y master se actualizan, debería rebase origin/b1 en origin/mastery luego hacer git pull cuando b1 ¿Se verifica para extraer el rebase al repositorio local?
Dror
No, no necesitas un git pull y nunca rebasa una rama remota (por ejemplo, origin/master ) en cualquier otra cosa.
Frerich Raabe
@FrerichRaabe Entonces, ¿cuál es el camino correcto aquí? Las advertencias que uno recibe significan que después de la rebase origin/b1 y b1 no son lo mismo. Esto es bastante obvio, pero ¿cuál es la forma correcta de solucionarlo? ¿O arreglarlo significa desordenar la historia como se explicó @heavyd?
Dror
1
Yo agregaría siempre y cuando ese repo sea el único que esté presionando / tirando del repositorio remoto público que esté a salvo. Me he encontrado con problemas en los que era el único que estaba trabajando en un proyecto, pero estoy trabajando desde dos máquinas diferentes. Si alguna vez las ramas divergen en esa situación, todavía puede tener un lío.
heavyd

Respuestas:

7

La razón por la que no desea volver a generar las confirmaciones que ha enviado a un repositorio público es porque git-rebase El comando cambia la historia.

Ahora, ¿qué significa eso y por qué es malo? Primero, sugeriría leer esta sección del libro git. A partir de eso, aprenderá que las confirmaciones consisten en un puntero a un objeto de árbol (instantánea de los archivos) y un puntero a la confirmación principal. Ahora, cuando "cambia el historial" volviendo a basar las confirmaciones sobre las nuevas confirmaciones, está cambiando el puntero principal de las confirmaciones que ya ha realizado, lo que a su vez cambia el ID de sus confirmaciones.

La razón por la que esto es malo es que si comparte sus compromisos públicamente, y otros comienzan un trabajo adicional basado en esos compromisos, luego realiza un cambio en esos compromisos, sus árboles ya no están sincronizados.

Todo esto se puede ver emitiendo git-log Comandos mientras realizas tu ejemplo. Corrí estos justo antes de ejecutar el comando rebase:

$ git log --pretty=oneline origin/master
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline origin/b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

$ git log --pretty=oneline b1
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Y ahora después de realizar la rebase, origin/master y origin/b1 son lo mismo, pero b1 es ahora:

$ git log --pretty=oneline b1
6687c64c37db0ee21a4d87e45d6ccb0913b8686d added foo2 in b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Notará que la confirmación "foo2 agregado en b1" tiene una ID diferente a la de los comandos de registro anteriores. Si confirma este cambio en su repositorio público, ahora tiene dos confirmaciones que tienen el mismo trabajo realizado en ellos y eso causa problemas.

Ahora suponga que, en lugar de volver a basar b1 en la parte superior del maestro, simplemente fusionó el maestro en b1, su registro se vería así:

$ git checkout b1
$ git merge origin/master
$ git log --pretty=oneline b1
518eb2dc6b2da0ff43ddd6837332031cc00eaad1 Merge remote-tracking branch 'origin/master' into b1
9b077261d1619803213201d5c7cefb757eb66b67 Changed foo in master
63a57ef54e301314a9dab38de0cd9d88c59a5fba added foo2 in b1
911ce5b247e79682ec9f73ad9a15fd3167b7e76d Added foo

Notará la confirmación adicional que representa la combinación de las dos confirmaciones anteriores. Esta historia ahora puede ser compartida y todos serán felices.

git-log --graph También puede ayudar a arrojar algo de luz adicional sobre lo que está sucediendo.

heavyd
fuente
¿Puede por favor referirse al método descrito en la publicación vinculada? Además, ¿qué pasa si Lo sé que el origin/b1 solo lo uso yo mismo - asumiendo que es una especie de respaldo de b1. ¿Qué efecto tiene este supuesto sobre la situación? ¿Tiene sentido en este caso hacer? git push --force ¿O algo similar justo después de la rebase?
Dror
Honestamente, el flujo de trabajo descrito en tu publicación es un montón de trabajo adicional. Debes ser capaz de lograr lo mismo simplemente ejecutando git merge master cuando el foo La sucursal está desprotegida. De nuevo, pruébalo por ti mismo y usa git-log para ver que esta pasando
heavyd
De hecho, pero hay un gran inconveniente con esta fusión, normalmente master Se considera una rama estable mientras que foo (o cualquier otra rama) no es estable. La fusión de estable en inestable es indeseable, por lo tanto, el trabajo adicional. Estoy tratando de averiguar si hay accesos directos o no.
Dror
1
¿Puede explicar por qué la fusión de estable en inestable no es deseable? Eso no debería desestabilizar la rama más de lo que ya está.
heavyd
Tal vez "no deseado" era una redacción demasiado fuerte. Si siempre fusionas inestable en estable, al final del día, cuando la rama está lista y estable por sí misma, es solo una (!) Fusión de nuevo al maestro. Esto produce una historia más limpia. Quizás una mejor redacción sería que este enfoque tiene la ventaja anterior.
Dror
3

Tarde en la fiesta, pero aquí está la respuesta para la posteridad:

git rebase está destinado a ser utilizado localmente. Reescribe el historial, lo que permite una "línea principal" muy agradable, pero es peligroso en un entorno multiusuario.

Si:

  • eres el único que usa el repositorio remoto,
  • Y lo estás utilizando en un solo espacio de trabajo,

Entonces eso podría tiene sentido hacer esto por forzando La historia a reescribir. Haga su rebase y luego:

rama remota de origen git push -f

Si alguna de estas suposiciones no se cumplió, puede vivir para lamentar esta acción :-)

Lee más en esta entrada de blog

Omri Spector
fuente