J'essaie d'apprendre la nouvelle commande git-subtree qui a été ajoutée dans Git 1.7.11. Je semble perdre la capacité de rebaser 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 contient é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'historique 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, lorsque je veux rebaser mon référentiel contre Origin/master
, il échoue car le commit squash est appliqué directement au commit parent qui ne s'applique pas, car il est appliqué à la racine du référentiel et non au préfixe que je lui ai donné lors de l'ajout. le sous-arbre.
La raison en est assez claire si je regarde le commit de squash. Il n'y a aucune information sur le préfixe. Il s’agit simplement des commits mylib originaux écrasés. Seul le prochain engagement de fusion en sait quelque chose, mais rebase ne le prend pas en compte ici.
Existe-t-il des solutions de contournement (à part ne jamais redéfinir les commits de la sous-arborescence)?
Ce n'est pas une solution, mais c'est le travail actuel que j'utilise ...
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 de manière interactive sur la 2ème validation avant que la sous-arborescence ajoute:
$ git rebase -i 020e372
Supprimez les deux entrées de sous-arborescence et marquez l'édition pour la validation précédente:
e b99d55b Add readme
Enregistrez le fichier/fermer, puis quand il arrive à la validation "Ajouter un readme", exécutez la commande amende:
$ git commit --amend
Puis rajoutez votre nouvelle sous-arborescence:
$ git subtree add -P lib/mylib myliborigin master
Continuez la rebase:
$ git rebase --continue
Votre branche devrait alors être rebasée par rapport au maître et le sous-arbre sera "normal" avec le Squash + le Merge intact:
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
C'est une vieille question, mais j'avais juste le même problème avec mon repo, et j'ai finalement trouvé une solution complète, qui (j'espère) préserve toutes les métadonnées de la sous-arborescence.
Supposons que nous ayons cet arbre de commit:
B (master) Add README.md
|
A Initial commit
et nous avons créé une branche feature
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
Cela crée un engagement de fusion D avec deux parents: notre master
actuelle (B) et un engagement non lié F
avec l'historique écrasé du référant externe. Cette validation contient également des métadonnées 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 branches de manière indépendante.
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 reformer feature
en master
. Voici comment:
feature
, une par une, pour créer une nouvelle copie de la branche feature
au-dessus de master
.git branch -f feature C
git checkout feature
git cherry-pick D E
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
Nous avons maintenant l'équivalent d'une base de base, mais nous avons perdu toutes les informations sur le référentiel externe, requises pour git subtree
. Pour le restaurer:
feature
pour le rendre permanent.git checkout feature
git replace --graft D' C F
git filter-branch --tag-name-filter cat -- master..
Et maintenant, nous obtenons une image exactement équivalente à celle qui a commencé avec. Les anciens commits D et E sont toujours disponibles, mais ils peuvent être ramassés ultérieurement.
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: Ceci réécrit l’historique de feature
. Méfiez-vous donc de le publier si quelqu'un d'autre collabore avec vous sur cette branche. Cependant, puisque vous vouliez faire une rebase en premier lieu, vous en êtes probablement conscient :-)
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 appliqué automatiquement votre sous-arbre. Cela vous met dans la situation décrite par @ericpeters dans sa réponse. Solution:
Ajoutez de 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 la rebase:
git rebase --continue
Et vous êtes tous ensemble!
Si vous vous demandez si cela a fonctionné avec succès, comparez-le à votre version originale après refonte de la base pour vous assurer de ne rien changer:
git diff <ref-before-rebase> <ref-after-rebase> -- .
(le -- .
à la fin indique à git de ne placer que diff
les fichiers de votre répertoire actuel).
Si tout échoue et que vous ne vous souciez pas de préserver les commits, vous pouvez simplement git cherry-pick
le commit de la sous-arborescence d'origine.
Le message de validation ressemblera à quelque chose comme Add 'lib/' from commit '9767e6...'
- c'est celui que vous voulez.
Apparemment, il s'agit du 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 à personne. J'aimerais trouver une solution de contournement pour cela aussi.
J'avais un problème similaire: je voulais créer une nouvelle base après avoir ajouté une sous-arborescence et utiliser --preserve-merges me laissait toujours avec un conflit de fusion (en raison de fichiers .gitignore
en conflit et autres).
Dans mon cas, je n’avais pas prévu d’utiliser une fonctionnalité de sous-arbre: j’ai simplement importé un dépôt 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, à partir d'autres liés _ { réponses j'ai trouvé.
Supposons que j'ai deux projets, main_project et sub_project, dans le même répertoire. Je souhaite extraire sub_project dans un répertoire nommé sub_project dans main_project, en supposant qu'aucun des référents n'ait jamais eu de 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.
Vous devez utiliser
git rebase --preserve-merges --preserve-committer --onto new_place start end