Git: ¿Cómo rebase a un commit específico?

154

Me gustaría cambiar la base a un commit específico, no a un HEAD de la otra rama:

A --- B --- C          master
 \
  \-- D                topic

a

A --- B --- C          master
       \
        \-- D          topic

en vez de

A --- B --- C          master
             \
              \-- D    topic

¿Cómo puedo lograr eso?

Ondra Žižka
fuente
44
¿Has intentado hacerlo git checkout Bantes de correr git rebase?
Peter-Paul van Gemerden
No, ¿eso debería ayudar? Supongo que solo las referencias del rebasecomando son lo que importa.
Ondra Žižka

Respuestas:

98

Puede evitar el uso del parámetro --onto haciendo una rama temporal en el commit que desee y luego use rebase en su forma simple:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Adam Dymitruk
fuente
55
Me gusta más este enfoque tipo RISC :) Lo intentaré. Gracias.
Ondra Žižka
10
Me pregunto por qué no funciona para mí, en un escenario ligeramente diferente . Quiero que el grupo se salte el pep8 y se base en master . git rebase temp(cuando está en grupo ) se da por vencido con "Los grupos de sucursales actuales están actualizados".
Alois Mahdal
44
Esta solución no funcionará para el escenario en el que el tema ya ha sido modificado en master, pero desea cambiarlo en un ancestro para masterizar. En ese caso, debe usarlo git rebase --onto <target> <from> <to>para poder especificar <from> commit.
mirzmaster
Esta es, con mucho, la opción más fácil si desea volver a crear el mismo compromiso en el que se basa la rama.
Ash
1
Parece que trabajo, pero el GitLab dice algo más. Si tenía 10 commits detrás, 5 commits adelante, mi expectativa era tener 8 commits atrás, 5 commits adelante después de obtener 2 commits. Pero en lugar de esto, agrega más compromisos a esos 5.
ROMANIA_engineer
67

Incluso puedes tomar un enfoque directo:

git checkout topic
git rebase <commitB>
r0hitsharma
fuente
66
Para mí, esto en realidad no hace lo que se pretende. Por lo que puedo decir, intenta basarse en el "último antepasado común" de topicy commitB.
Dan Lenski el
2
@DanLenski, así no es como funciona el rebase. Citando los documentos , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. lo intenté de nuevo ahora y parecía funcionar bien.
r0hitsharma
1
Súper fácil y funciona! Ahora tengo: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek
No me funcionó. Antes de esto, GitLab dijo "n se compromete por delante". Y ahora, dice "m commits ahead" dónde m > n.
ROMANIA_engineer
48

Use la opción "sobre":

git rebase --onto master^ D^ D
Adam Dymitruk
fuente
2
Dy D^sería hash de la última y próxima a la última confirmación de "tema"?
Ondra Žižka
39
La sintaxis es como git rebase --onto <new-parent> <old-parent>. Consulte Configuración del puntero padre git a un padre diferente . En su caso, <nueva-parent> es B, y <pasada de padres> es A.
JSZ
77
Siempre uso los 3 argumentos: desitnation, inicio y fin de commits para rebase.
Adam Dymitruk
14
Esto funcionó para mí:git rebase --onto <commit-ID> master
44
El comentario de @ jsz es correcto, contrario al comentario de Simon South, es al revés: git rebase --onto master <commit-ID-of-old-parent>y para OP git rebase --onto B A.
Gaborous
19

El comentario de jsz anterior me ahorró un montón de dolor, así que aquí hay una receta paso a paso basada en ella que he estado usando para cambiar la base / mover cualquier confirmación encima de cualquier otra confirmación:

  1. Encuentre un punto de ramificación anterior de la rama que se va a volver a armar (mover): llámelo padre antiguo. En el ejemplo anterior, eso es A
  2. Busque commit sobre el cual desea mover la rama, llámelo nuevo padre. En el examen eso es B
  3. Debes estar en tu rama (la que mueves):
  4. Aplica tu rebase: git rebase --onto <new parent> <old parent>

En el ejemplo anterior, es tan simple como:

   git checkout topic
   git rebase --onto B A
Néstor Milyaev
fuente
66
Esta debería ser la respuesta correcta. Excepto que uso git rebase --onto B master, vea mi respuesta para una explicación más completa.
Zack Morris
No funcionó bien para mí. Elegí 2 confirmaciones consecutivas (la última del maestro que estaba en la rama actual y la primera del maestro que no estaba en la rama actual). Comencé con 100 detrás - 10 adelante y en lugar de tener 99 atrás - 10 adelante, ahora tengo 105 atrás - 13 adelante.
RUMANIA_engineer
Lamento escuchar que no funcionó. Parece que sus ramas divergieron un poco: sugeriría aplastar primero antes de intentar cambiar las ramas con tantas diferencias.
Nestor Milyaev
11

Solución de tema

El comando correcto para responder la pregunta publicada podría ser cualquiera de los siguientes (suponiendo que la rama topicya esté desprotegida):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Si topicno está desprotegido, simplemente agregue topicel comando (excepto el último) así:

git rebase --onto B master topic

Alternativamente, revise la sucursal primero con:

git checkout topic

Rebase cualquier cadena de confirmaciones a una confirmación de destino

La forma básica del comando que necesitamos, basada en la documentación, es:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>es opcional y todo lo que hace es verificar la rama especificada antes de ejecutar el resto del comando. Si ya ha desprotegido la rama que desea cambiar, entonces no necesita esto. Tenga en cuenta que debe haber especificado <Upstream>para especificar <Branch>o git pensará que está especificando <Upstream>.

<Target>es la confirmación a la que adjuntaremos nuestra cadena de confirmaciones. Al proporcionar un nombre de sucursal, simplemente está especificando la confirmación principal de esa rama. <Target>puede ser cualquier confirmación que no se incluirá en la cadena de confirmaciones que se mueven. Por ejemplo:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Para mover toda la rama de la característica, no se puede seleccionar X, Y, Zo featurecomo el <Target>ya que los todos son commits dentro del grupo que está siendo movido.

<Upstream>es especial porque puede significar dos cosas diferentes. Si se trata de una confirmación que es un antepasado de la rama desprotegida, entonces sirve como punto de corte. En el ejemplo que he proporcionado, esto podría ser cualquier cosa que no es C, Do master. Todos los commits posteriores <Upstream>hasta el encabezado de la rama desprotegida son los que se moverán.

Sin embargo, si <Upstream>no es un ancestro, entonces git realiza una copia de seguridad de la cadena desde el commit especificado hasta que encuentre un ancestro común con la rama desprotegida (y aborta si no puede encontrar uno). En nuestro caso, una <Upstream>de B, C, D, o masterse traducirán en cometer Bservir como punto de corte. <Upstream>es en sí mismo un comando opcional y si no se especifica, entonces git mira al padre de la rama desprotegida que es el equivalente de ingresar master.

Ahora que git ha seleccionado las confirmaciones que cortará y moverá, las aplicará para <Target>omitir las que ya se hayan aplicado al objetivo.

Ejemplos y resultados interesantes

Usando este punto de partida:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Se aplicará compromete B, C, X, Y, Zpara cometer Dy terminan saltando By Cdebido a que ya se han aplicado.

  • git rebase --onto C X feature
    Aplicará commits Yy Zcommit C, eliminando efectivamente commitX

Isaac Brown
fuente
4

Una solución más simple es git rebase <SHA1 of B> topic. Esto funciona independientemente de dónde HEADesté.

Podemos confirmar este comportamiento desde git rebase doc

<upstream>Rama aguas arriba para comparar. Puede ser cualquier confirmación válida , no solo un nombre de sucursal existente. El valor predeterminado es el ascendente configurado para la rama actual.


Quizás esté pensando qué sucederá si también menciono SHA1 topicen el comando anterior.

git rebase <SHA1 of B> <SHA1 of topic>

Esto también funcionará, pero el rebase no hará referencia Topica una nueva rama así creada y HEADestará en estado separado. Entonces, desde aquí, debe eliminar manualmente la antigua Topicy crear una nueva referencia de rama en la nueva rama creada por rebase.

Número945
fuente
3

He usado una mezcla de soluciones descritas anteriormente:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Me resultó mucho más fácil de leer y entender. La solución aceptada me llevó a un conflicto de fusión (demasiado flojo para arreglarlo a mano):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
malat
fuente
1
mismo aquí, varios archivos tuvieron conflictos cuando usé las 2 respuestas más populares (es decir, por r0hitsharma y Dymitruk)
Oliver
3

Dado que el rebase es tan fundamental, aquí hay una expansión de la respuesta de Nestor Milyaev . La combinación de los comentarios de jsz y Simon South de la respuesta de Adam Dymitruk produce este comando que funciona en la topicrama independientemente de si se ramifica desde el mastercommit de la rama Ao C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Tenga en cuenta que se requiere el último argumento (de lo contrario, rebobina su rama para confirmar B).

Ejemplos:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Entonces, el último comando es el que normalmente uso.

Zack Morris
fuente
-2

Hay otra forma de hacerlo o si desea volver a más de un solo commit.

Aquí hay un ejemplo para volver a n número de confirmaciones:

git branch topic master~n

En aras de esta pregunta, esto también se puede hacer:

git branch topic master~1

El comando funciona perfectamente git version 2.7.4. No lo he probado en ninguna otra versión.

Talha Ashraf
fuente
¿Malinterpretaste esto como una pregunta sobre ramificación? Esta es realmente una pregunta sobre rebase.
NetherGranite