web-dev-qa-db-fra.com

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

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)?

54
Epeli

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
9
ericpeters

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:

1. Sélectionnez les validations dans 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:

2. Ajoutez le lien parent manquant sous forme de greffe et réécrivez l’historique de 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 :-)

6
Skiminok

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.

3
Kevin Cooper

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.

3
Nick Hutchinson

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.

2

Vous devez utiliser

git rebase --preserve-merges --preserve-committer --onto new_place start end
0
Adam Dymitruk