Fusionar cambios del repository copydo sin verdadero antecesor común en git

Tengo un proyecto, DemoA que se creó a partir de un repository de git, Project1.

Desafortunadamente, DemoA comenzó simplemente como una copy de los files de Project1, antes de convertirse en un proyecto real a largo ploop. Ahora me gustaría hacer de Project1 un submodule de DemoA, pero, lo que es más importante, quiero fusionar los cambios realizados en el código derivado de Project1, en DemoA.

He hecho un subtree dividido en DemoA para crear una twig P1, que tiene todos los cambios realizados en la base de código de Project1 en DemoA.

También logré agregar los cambios a Project1 realizados en DemoA antes de que se creara una instancia como repository.

Project1 A - B - C - D - E Demo1/P1 (untracked changes) F - G - H - I where the files in E are identical to F 

Lo que quiero:

 Project 1 A - B - C - D - E - G - H - I 

Obviamente, los hashes para E y F son diferentes, así que cuando agregué Demo1 / P1 como un control remoto a Project1 e intenté fusionarme, no me quejé de ningún ancestro común.

He intentado usar format-patch , pero git am se ha quejado

error: file.xyz: ya existe en el índice

y estaba tratando de volver a basarme en una twig diferente , haciendo:

 git rebase -s recursive -X subtree=project1dir --onto (E-hash) (F-hash) emptybranch 

pero claramente no entiendo lo que está haciendo en realidad, ya que no parecía hacer nada realmente.

¿Hay una manera limpia de hacer esto? No me importa algo de manualidad en el process, pero me gustaría preservar la historia.

Todo esto es moderadamente difícil (el nivel de dificultad real varía según las circunstancias y su familiaridad con Git).

Si los files en E y F son realmente idénticos, la (o una) manera fácil de hacerlo sería poner un injerto (con git replace o el file de injertos) para que Git finja que el compromiso principal de G es commit E . Es decir, tienes:

 A--B--C--D--E <-- master F-------------G--H--I <-- refs/remotes/rem/P1 

y git diff master rem/P1~4 no produce salida en absoluto (los nombres master confirman E , rem/P1~4 nombres confirman F , y los dos treees para E y F coinciden exactamente).

Usted desea , al less como producto intermedio quizás, que tenía esto:

 A--B--C--D--E <-- master \ F G--H--I <-- refs/remotes/rem/P1 

Es decir, a usted le gustaría que Git finja, al less para algunos propósitos y un período de time, que el compromiso G ha confirmado que E es su padre.

Usando git replace para emular los viejos injertos horribles

Los injertos de Git hacen exactamente eso: le dicen a Git que pretenda que el padre (s) de algún compromiso es algún otro compromiso (s). Pero estos han sido desaprobados a favor del git replace más genérico. Puedes usar git replace para hacer un nuevo commit G' que se asemeja (pero reemplaza, al less, a la mayoría de los commands de Git) G , con la única diferencia de que G' tiene E como su padre.

Luego puede usar git filter-branch para volver a copyr commits en el repository para que este reemploop se vuelva real y permanente, en lugar de solo una copy. Obtendrás, por supuesto, nuevos hashes de confirmación para las nuevas confirmaciones ( G' puede mantener su hash pero debes get una nueva H' e I' ). Vea esta respuesta por Jakub Narębski , y luego ¿Cómo se diferencian los injertos e injertos de git? (¿Los injertos ahora están en desuso?) , Donde VonC se vincula con las respuestas de Jakub.

(Los injertos de Git todavía funcionan, y usted puede simplemente poner el hash para los commits G y E en .git/info/grafts : echo $(git rev-parse rem/P1~3) $(git rev-parse master) > .git/info/grafts , por ejemplo. Pero son un truco horrible y si haces este tipo de truco es mejor simplemente ejecutar tu twig de filter inmediatamente después, como señala Jakub.)

Usando git rebase

También puedes usar git rebase --onto , como estabas intentando, pero debes comenzar esta rebase usando un nombre de twig existente (ordinario, local) (no estoy seguro de dónde viene la emptybranch desde aquí) que apunte a cometer I Creo que tal vez el paso que te falta podría estar haciendo este nombre ordinario ordinario de la sucursal:

 git checkout -b rewrite rem/P1 

por ejemplo, suponiendo que el nombre rem/P1 resuelve cometer I O git checkout -b rewrite <hash-of-I> , si tienes ese hash delante de ti para cortar / pegar fácilmente. En ese punto, tendrás esto:

 A--B--C--D--E <-- master F-------------G--H--I <-- HEAD -> rewrite, rem/P1 

Es decir, ahora estás en esta nueva twig de rewrite , que apunta a cometer I Ahora puede git rebase --onto master HEAD~3 para copyr las 3 confirmaciones más recientes en la twig actual – G , H e I Las copys serán G' , H' e I' , siendo el padre de G' E -el compromiso al que apunta el master y el padre de H' siendo G' y así sucesivamente:

  G'-H'-I' <-- HEAD -> rewrite / A--B--C--D--E <-- master F-------------G--H--I <-- rem/P1 

Ahora puede eliminar el control remoto y su bifurcación de seguimiento remoto ya que tiene la cadena de compromiso que desea. También puede avanzar rápidamente master a punto para confirmar I' en cualquier momento, si eso es lo que desea.