tag - Subárbol Git-Subárbol actualizado pero no puede empujar
git tags best practices (10)
En Windows, el comando anidado no funciona:
git push heroku `git subtree split --prefix pythonapp master`:master --force
Puede ejecutar el bit anidado primero:
git subtree split --prefix pythonapp master
Esto (después de una gran cantidad de números) devolverá un token, por ej.
157a66d050d7a6188f243243264c765f18bc85fb956
Use esto en el comando contenedor, por ejemplo:
git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
Estoy usando el subárbol de Git con un par de proyectos en los que estoy trabajando, para compartir un código base entre ellos. El código base se actualiza a menudo, y las actualizaciones pueden ocurrir en cualquiera de los proyectos, y eventualmente todos se actualizan.
Me encontré con un problema donde git informa que mi subárbol está actualizado, pero se rechaza empujar. Por ejemplo:
#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch master -> FETCH_HEAD
Already up-to-date.
Si presiono, debería recibir un mensaje de que no hay nada que empujar ... ¿Verdad? ¿DERECHO? :(
#! git subtree push --prefix=public/shared project-shared master
git push using: project-shared master
To [email protected]:***
! [rejected] 72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to ''[email protected]:***''
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. ''git pull'')
hint: before pushing again.
hint: See the ''Note about fast-forwards'' in ''git push --help'' for details.
¿Cuál podría ser la razón de ésto? ¿Por qué está fallando el empuje?
Encontré la respuesta en este blog https://coderwall.com/p/ssxp5q
Si te encuentras con "Las actualizaciones fueron rechazadas porque la punta de tu rama actual está atrás. Fusiona el problema de los cambios remotos (p. Ej., ''Git pull'')" cuando estás presionando (por cualquier razón, especialmente por el historial de git) entonces necesitarás anidar los comandos de git para que puedas forzar un empuje hacia heroku. por ejemplo, dado el ejemplo anterior:
git push heroku `git subtree split --prefix pythonapp master`:master --force
Esto se debe a la limitación del algoritmo original. Cuando se manejan merge-commits, el algoritmo original usa un criterio simplificado para cortar padres no relacionados. En particular, verifica, si hay un padre, que tiene el mismo árbol. Si se encuentra dicho padre, se colapsaría la combinación de fusión y se usaría la confirmación primaria en su lugar, suponiendo que otros padres tuvieran cambios no relacionados con el subárbol. En algunos casos, esto provocaría la pérdida de partes de la historia, que tiene cambios reales en el subárbol. En particular, arrojaría secuencias de confirmaciones, que tocarían un subárbol, pero daría como resultado el mismo valor de subárbol.
Veamos un ejemplo (que puedes reproducir fácilmente) para comprender mejor cómo funciona esto. Considere el siguiente historial (el formato de línea es: asunto [árbol] commit):
% 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, estamos dividiendo en dir
. Los Compromisos D
y E
tienen el mismo árbol z
, porque hemos cometido C
, que deshizo el compromiso B
, por lo que la secuencia BC
no hace nada para el dir
aunque tenga cambios en él.
Ahora vamos a dividir. Primero nos separamos en commit 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
Luego nos separamos en commit E
% git log `git subtree split -P dir E` ...
* D'' [z''] add dir/file2.txt
* A'' [w''] add dir/file1.txt
Sí, perdimos dos commits. Esto da como resultado el error al intentar empujar la segunda división, ya que no tiene esos dos commits, que ya llegaron al origen.
Por lo general, puede tolerar este error utilizando push --force
, ya que los commits caídos generalmente no tendrán información crítica en ellos. A largo plazo, el error debe corregirse, por lo que el historial de división en realidad tendría todas las confirmaciones, que tocar dir
, como se esperaba. Me gustaría que la solución incluyera un análisis más profundo de las confirmaciones principales 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
# an identical parent could be used in place of this rev.
identical="$parent"
else
nonidentical="$parent"
fi
...
if [ -n "$identical" ]; then
echo $identical
else
copy_commit $rev $tree "$p" || exit $?
fi
La respuesta de Eric Woodruff no me ayudó, pero lo siguiente fue:
Normalmente ''git subtree pull'' con la opción ''--squash''. Parece que esto hizo que las cosas fueran más difíciles de reconciliar, por lo que tuve que hacer una extracción del subárbol sin aplastar esta vez, resolver algunos conflictos y luego presionar.
Debo agregar que el tirón aplastado no reveló ningún conflicto, me dijo que todo estaba bien.
Otra solución [más simple] es avanzar la cabeza del control remoto haciendo otra confirmación si es posible. Después de tirar de esta cabeza avanzada en el subárbol local, podrá volver a presionarlo.
Para una aplicación de tipo "Páginas GitHub", donde despliega un subárbol "dist" en una rama gh-páginas, la solución puede parecerse a esto
git push origin `git subtree split --prefix dist master`:gh-pages --force
Menciono esto ya que se ve ligeramente diferente de los ejemplos de heroku dados anteriormente. Puedes ver que mi carpeta "dist" existe en la rama principal de mi repositorio, y luego la empujo como un subárbol a la rama gh-pages que también está en el origen.
Puede forzar el envío de cambios locales al repositorio de subárboles remotos
git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
También he encontrado este problema antes, y así es como lo resolví.
Lo que descubrí fue que tenía una sucursal que no estaba vinculada a la sucursal maestra local. Esta rama existe y simplemente está colgando en el vacío. En su caso, probablemente se llame project-shared
. Asumiendo que este es el caso y cuando haces una git branch
puedes ver una sucursal project-shared
local, luego puedes '' agregar '' nuevas confirmaciones a tu sucursal project-shared
existente haciendo un:
git subtree split --prefix=public/shared --onto public-shared --branch public-shared
La forma en que entendí es que el git subtree
comenzará a crear una nueva rama desde --onto
, en este caso es la rama local public-shared
. Entonces la rama significa crear una rama, que simplemente reemplaza la antigua rama public-shared
.
Esto mantendrá todos los SHA anteriores de la rama public-shared
. Finalmente, puedes hacer una
git push project-shared project-shared:master
Suponiendo que también tiene un control remoto project-shared
; esto impulsará el bloqueo local en la rama project-shared
nulo a la rama master
del project-shared
remoto project-shared
.
Un powerShell rápido para usuarios de Windows basado en la solución de Chris Jordan
$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
Use la bandera --onto
:
# DOESN''T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master
[EDITAR: desafortunadamente la subtree push
--onto
no reenvía --onto
a la split
subyacente, por lo que la operación tiene que hacerse en dos comandos. Una vez hecho esto, veo que mis comandos son idénticos a los de una de las otras respuestas, pero la explicación es diferente, así que lo dejaré aquí de todos modos.]
git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master
O si no estás usando bash:
git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master
Pasé horas estudiando minuciosamente la fuente del subtítulo git para entender esto, así que espero que lo aprecien;)
subtree push
inicia al ejecutar la subtree split
, que reescribe el historial de confirmación en un formato que debe estar listo para enviar. La forma en que lo hace es eliminar las public/shared/
fuera de la ruta que lo tiene y eliminar cualquier información sobre los archivos que no la tienen. Eso significa que, incluso si tira no aplastado, todas las confirmaciones de sub-repositorio en sentido ascendente se descartan ya que nombran los archivos por sus sendas vacías. (Los commits que no tocan ningún archivo en public/shared/
, o merge commits que son idénticos a uno de los padres, también se colapsan. [EDIT: también, desde entonces he encontrado algo de detección de squash, así que ahora estoy pensando que es solo si sacaste no aplastado, y luego la fusión simplista cometer un colapso descrito en otra respuesta más se las arregla para elegir la ruta no aplastada y descartar la ruta aplastada.]) El resultado es que las cosas que intenta empujar terminan conteniendo cualquier trabaje con alguien comprometido con el repositorio de host actual desde el que está trabajando, pero no con personas de trabajo comprometidas directamente con el repositorio secundario o mediante otro repositorio de host.
Sin embargo, si usas --onto
, entonces todas las confirmaciones ascendentes se registran como OK para usar textualmente, por lo que cuando el proceso de reescritura las encuentre como uno de los padres de una fusión que desea reescribir, las mantendrá en lugar de intentarlo para reescribirlos de la manera habitual.