Igualar dos ramas en git / sobreescribir una rama en otra
Normalmente utilizaríamos la opción 1 o 2 que aparecen a continuación, empezar una rama nueva o borrar los commis existentes con un reset:
1. Don’t bother. Abandon branch
A
, make a newA2
, and use that.2. Use
git reset
or equivalent to re-pointA
elsewhere.Methods 1 and 2 are effectively the same in the long run. For instance, suppose you start by abandoningA
. Everyone develops onA2
instead for a while. Then, once everyone is usingA2
, you delete the nameA
entirely. Now thatA
is gone, you can even renameA2
toA
(everyone else using it will have to do the same rename, of course). At this point, what, if anything, looks different between the case where you used method 1 and the case where you used method 2? (There is one place you may still be able to see a difference, depending on how long your «long run» is and when you expire old reflogs.)3. Make a merge, using a special strategy (see «alternative merge strategies» below). What you want here is
https://stackoverflow.com/a/36321787-s theirs
, but it doesn’t exist and you must fake it.
La segunda opción no vale si ya hemos hecho push, porque tendríamos que hacer --force
y fastidiaríamos a los demás. La primera opción está bien pero quizá pueda ser un poco confuso para los demás. En mi trabajo utilizamos el identificador de la tarea del Jira como nombre de la rama, y hacer feature/proyecto-XXXX-2
es salirse un poco de esa norma. La gente va a piñón haciendo checkout de feature/proyecto-XXXX
y se descargarían la antigua, así que también quería evitar este método. ¿Se puede seguir con la rama antigua añadiendo un commit que iguale a la otra?
El propósito de git merge es, como indica su nombre, juntar el código de dos ramas. Salvo para conflictos, no existe ninguna rama «dominante»:
The modifier «dominant» is not defined by Git. The way you use the word appears to me to make an incorrect assumption, which I think makes the question un-answerable as is. With one small change, though, the answer becomes simple: it’s neither. No branch is «dominant» here; both branches are equal partners, and the result of the merge is the same, whether you merge A into B, or B into A—but you can change this, in several ways.
https://stackoverflow.com/a/42104116
Si tenemos un fichero con una sola línea y dos ramas diferentes:
If we changed line 1 and they didn’t, the merge result is our version.
https://stackoverflow.com/a/42104116
If we didn’t change line 1, and they did, the merge result is their version.
If we both changed line 1, the merge result is that the merge fails, with a merge conflict. Git writes both lines into the file and stops the merge with an error, and makes us clean up the mess:
Como explica ahí, si nuestra rama ha cambiado un fichero que no se ha modificado en la otra, git seguirá tomando la nuestra. Si simplemente queremos «igualar» a la otra, esto no nos vale.
Las opciones X ours/theirs tampoco nos valen porque sólo se aplican en caso de conflicto:
While
https://stackoverflow.com/a/36321787git merge
‘s defaultrecursive
strategy has-X
optionsours
andtheirs
, they do not do what we want here. These simply say that in the case of a conflict, git should automatically resolve that conflict by choosing «our change» (-X ours
) or «their change» (-X theirs
).
Finalmente, encontré aquí una solución: (aquí intenta igualar A, que es la rama «mala», a B):
As outlined in this other answer, there are a number of ways to force git to effectively implement
-s theirs
. I think the simplest to explain is this sequence:
git checkout A
git merge --no-commit -s ours B
git rm -rf . # make sure you are at the top level!
git checkout B -- .
git commit
The first step is to ensure that we are on branch
A
, as usual. The second is to fire up a merge, but avoid committing the result yet (--no-commit
). To make the merge easier for git—this is not needed, it just makes things faster and quieter—we use-s ours
so that git can skip the diff steps entirely and we avoid all merge conflict complaints.At this point we get to the meat of the trick. First we remove the entire merge result, since it is actually worthless: we do not want the work-tree from the tip commit of
A
, but rather the one from the tip ofB
. Then we check out every file from the tip ofB
, making it ready to commit.Last, we commit the new merge, which has as its first parent the old tip of
A
and as its second parent the tip ofB
, but has the tree from commitB
.If the graph just before the commit was:
...---o--...--o--o <-- A
/ /
...-o--...--*--o--o <-- B
then the new graph is now:
...---o--...--o--o--o <-- A
/ / /
...-o--...--o--o--* <-- B
The new merge-base is the tip of
https://stackoverflow.com/a/36321787B
as usual, and from the perspective of the commit graph, this merge looks exactly like any other merge. What’s unusual is that the source tree for the new merge at the tip ofA
exactly matches the source tree for the tip ofB
.