Comment rebaser après l’ajout de git-subtree?

J’essaie d’apprendre la nouvelle commande git-subtree qui a été ajoutée à Git 1.7.11. Je semble perdre la capacité de rebase après avoir ajouté un sous-arbre. J’ai le référentiel principal avec le fichier README et un référentiel de bibliothèque qui a également un fichier README. Je l’ajoute au répertoire lib avec subtree add :

 $ git subtree add -P lib/mylib myliborigin master 

Cela fonctionne bien, mais maintenant, l’histoire ressemble à ceci:

 * 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - |\ | * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d * b99d55b Add readme * 020e372 Initial 

Maintenant, quand je veux rebaser mon repository contre origin/master et que cela échoue parce que la validation de squash est appliquée directement à la validation de son parent qui ne s’applique pas, parce qu’il est appliqué à la racine du repository et non au préfixe lors de l’ajout du sous-arbre.

La raison en est assez claire si je regarde le commit de squash. Il n’y a pas d’informations sur le préfixe. Ce sont juste les commits originaux de mylib écrasés ensemble. Seule la prochaine validation de fusion en sait quelque chose, mais rebase ne la prend pas en compte ici.

Existe-t-il des solutions de contournement (en dehors du rebasage des commits du sous-arbre)?

    Ce n’est pas une solution, mais c’est le travail actuel autour de moi …

    En utilisant votre exemple initial:

     * 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - |\ | * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d * b99d55b Add readme * 020e372 Initial 

    Rebase interactivement à la 2ème validation avant que le sous-arbre ajoute:

     $ git rebase -i 020e372 

    Supprimez les deux entrées de la sous-arborescence et modifiez la marque pour la validation précédente:

     e b99d55b Add readme 

    Sauvegardez le fichier / close, puis quand il arrive à la validation “Add readme”, lancez la commande de modification:

     $ git commit --amend 

    Ensuite, rajoutez votre nouveau sous-arbre:

     $ git subtree add -P lib/mylib myliborigin master 

    Continuez le rebase:

     $ git rebase --continue 

    Votre twig doit alors être rebasée de master, et le sous-arbre sera “normal” avec le squash + la fusion intacte:

     * 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - |\ | * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d 

    C’est une vieille question, mais j’ai juste eu le même problème avec mon repo, et j’ai finalement trouvé une solution complète, qui (espérons-le) préserve toutes les métadonnées du sous-arbre.

    Supposons que nous ayons cet arbre de validation:

     B (master) Add README.md | A Initial commit 

    et nous avons créé une twig avec un sous-arbre résidant dans lib/ :

     git remote add -f githublib https://github.com/lib/lib.git git subtree add --prefix lib/ githublib master --squash 

    Il crée un commit de fusion D avec deux parents: notre master actuel (B) et un commit non lié F avec l’historique écrasé du repo externe. Ce commit contient également des métadonnées de git subtree dans son message de validation (à savoir, git-subtree-dir et git-subtree-split ).

      D (feature) Merged commit 'F' as 'lib/' / \ / F Squashed 'lib/' content from GGGGGG B (master) Add README.md | A Initial commit 

    Plus tard, nous ajoutons des commits aux deux twigs indépendamment.

      E (feature) Remove .gitignore from lib/ C | (master) Add LICENSE.md | D Merged commit 'F' as 'lib/' | / \ |/ F Squashed 'lib/' content from GGGGGG B Add README.md | A Initial commit 

    Maintenant, nous voulons rebaser la feature sur le master . Voici comment:

    1. Choisissez les commandes de la feature une par une pour créer une nouvelle copie de la twig feature entités sur le master .

     git branch -f feature C git checkout feature git cherry-pick DE E' (feature) Remove .gitignore from lib/ | D' Merged commit 'F' as 'lib/' | | E Remove .gitignore from lib/ C | (master) Add LICENSE.md | D Merged commit 'F' as 'lib/' | / \ |/ F Squashed 'lib/' content from GGGGGG B Add README.md | A Initial commit 

    Maintenant, nous avons l’équivalent d’une rebase, mais nous avons perdu toutes les informations sur le repo externe, requirejses pour la git subtree . Pour le restaurer:

    2. Ajoutez le lien parent manquant comme greffon et réécrivez l’historique de l’ feature pour le rendre permanent.

     git checkout feature git replace --graft D' CF git filter-branch --tag-name-filter cat -- master.. 

    Et maintenant nous obtenons une image exactement équivalente à celle avec laquelle a commencé. Les anciens commits D et E sont toujours là, mais ils peuvent être récupérés plus tard.

     E' (feature) Remove .gitignore from lib/ | D' Merged commit 'F' as 'lib/' |\ | \ C \ (master) Add LICENSE.md | \ | \ | F Squashed 'lib/' content from GGGGGG B Add README.md | A Initial commit 

    Attention: Cela réécrit l’historique des feature , alors soyez prudent de le publier si quelqu’un d’autre collabore avec vous sur cette twig. Cependant, puisque vous vouliez faire un rebase en premier lieu, vous en êtes probablement conscient 🙂

    Apparemment, c’est le comportement attendu (pour une définition perverse du “comportement attendu”). Voir: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html .

    Non pas que cela aide beaucoup. J’aimerais trouver une solution de contournement pour cela aussi.

    J’ai eu un problème similaire: je voulais me rebaler après avoir ajouté un sous-arbre, et l’utilisation de –preserve- .gitignore me laissait toujours un conflit de fusion (dû à des fichiers .gitignore et autres).

    Dans mon cas, je n’avais pas forcément l’intention d’utiliser une fonctionnalité de sous-arbre: je tirais simplement un repository qui aurait dû faire partie du superprojet à l’origine. Au cas où cela aiderait quelqu’un d’autre, voici ce que j’ai fini par faire, basé sur d’autres réponses connexes que j’ai trouvées.

    Supposons que j’ai deux projets, main_project et sub_project, dans le même répertoire. Je veux placer sub_project dans un répertoire nommé sub_project dans main_project, en supposant qu’aucun repo n’ait jamais eu un répertoire nommé sub_project:

     cd main_project git fetch ../sub_project git checkout -b sub_project FETCH_HEAD git filter-branch --prune-empty --tree-filter ' if [[ ! -e sub_project ]]; then mkdir -p sub_project git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files sub_project fi' git checkout branch-to-merge-within git merge sub_project git branch -d sub_project 

    Je mettrai à jour si je trouve des problèmes avec cette approche.

    Cela fonctionne dans des cas simples:

     git rebase --preserve-merges master 

    Merci à @Techlive Zheng dans les commentaires.


    Vous pouvez voir

     fatal: refusing to merge unrelated histories Error redoing merge a95986e... 

    Ce qui signifie que git n’a pas pu appliquer automatiquement votre sous-arbre. Cela vous met dans la situation décrite par @ericpeters dans sa réponse. Solution:

    Ajoutez à nouveau votre sous-arbre (utilisez la même commande que vous avez utilisée à l’origine):

     git subtree add -P lib lib-origin master 

    Continuez le rebase:

     git rebase --continue 

    Et vous êtes tous ensemble!


    Si vous vous demandez si cela a fonctionné avec succès, vous pouvez comparer avec votre version d’origine après avoir changé votre nom pour vous assurer que vous n’avez rien changé:

     git diff   -- . 

    (le -- . à la fin indique à git de ne diff les fichiers de votre répertoire actuel).


    Si tout le rest échoue et que vous ne vous souciez pas de conserver les commits eux-mêmes, vous pouvez simplement git cherry-pick la sous-arborescence d’origine.

    Le message de validation ressemblera à Add 'lib/' from commit '9767e6...' – c’est celui que vous voulez.

    Vous devez utiliser

     git rebase --preserve-merges --preserve-committer --onto new_place start end