Esto se debe a la limitación del algoritmo original. Al manejar fusiones-confirmaciones, el algoritmo original usa un criterio simplificado para cortar a los padres no relacionados. En particular, comprueba si hay un padre que tiene el mismo árbol. Si tal padre lo encuentra, colapsaría la confirmación de fusión y usaría la confirmación padre en su lugar, asumiendo que otros padres tienen cambios no relacionados con el subárbol. En algunos casos, esto daría como resultado la eliminación de partes del historial, que tienen cambios reales en el subárbol. En particular, eliminaría secuencias de confirmaciones, que tocarían un subárbol, pero darían como resultado el mismo valor de subárbol.
Veamos un ejemplo (que puede reproducir fácilmente) para comprender mejor cómo funciona esto. Considere el siguiente historial (el formato de línea es: commit [árbol] asunto):
% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
* E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
* A [w] add dir/file1.txt
En este ejemplo, nos dividimos dir
. Confirma D
y E
tiene el mismo árbol z
, porque tenemos confirmación C
, que deshizo la confirmación B
, por lo que la B-C
secuencia no hace nada a dir
pesar de que tiene cambios.
Ahora hagamos la división. Primero nos dividimos en el compromiso C
.
% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt
A continuación, dividimos el compromiso E
.
% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt
Sí, perdimos dos confirmaciones. Esto da como resultado el error al intentar empujar la segunda división, ya que no tiene esas dos confirmaciones, que ya llegaron al origen.
Por lo general, puede tolerar este error mediante el uso push --force
, ya que las confirmaciones descartadas generalmente no contienen información crítica. A largo plazo, el error debe corregirse, por lo que el historial dividido en realidad tendría todas las confirmaciones, que se tocan dir
, como se esperaba. Esperaría que la solución incluyera un análisis más profundo de los compromisos de los padres para las dependencias ocultas.
Como referencia, aquí está la parte del código original, responsable del comportamiento.
copy_or_skip()
...
for parent in $newparents; do
ptree=$(toptree_for_commit $parent) || exit $?
[ -z "$ptree" ] && continue
if [ "$ptree" = "$tree" ]; then
identical="$parent"
else
nonidentical="$parent"
fi
...
if [ -n "$identical" ]; then
echo $identical
else
copy_commit $rev $tree "$p" || exit $?
fi