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:
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:
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