50 votes

Pourquoi je ne peux pas pousser à partir d'un clone peu profond ?

Le site git clone --depth L'option de commande dit

--depth <depth> 
Create a shallow clone with a history truncated to the specified number of revisions. 
A shallow repository has a number of limitations 
(you cannot clone or fetch from it, nor push from nor into it),
 but is adequate if you are only interested in the recent history of a large project with a long history,
 and would want to send in fixes as patches. 

Pourquoi les clones peu profonds ont-ils cette limitation ? Pourquoi s'agit-il d'un flux de travail uniquement basé sur des patchs ?

Pour certains flux de travail de projet, j'ai besoin de transmettre uniquement le dernier commit d'une branche unique à un codeur, puis de faire en sorte qu'il soit en mesure de push leurs développements (en avance rapide) sur le serveur principal. Ceci en partie pour la sécurité, la protection de la propriété intellectuelle et la taille du repo, et en partie pour réduire la confusion qu'un gros repo apporterait à un codeur naïf. Existe-t-il un workflow git qui permette cela ?


Mise à jour : Sur la base de la réponse de Karl Bielefeldt, l'équipe d'experts de la Commission européenne a décidé de mettre en place un système d'alerte précoce. git checkout --orphan devrait être la bonne réponse. Mais il faut encore "cloner" cette branche seule pour le nouvel utilisateur, et être capable de la pousser efficacement.

La page de manuel indique :

git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] --orphan

Créer une nouvelle branche orpheline, nommée <new_branch> à partir de <start_point> et passer dessus. Le premier commit effectué sur cette nouvelle branche n'aura pas de parents et sera la racine d'un nouvel historique totalement déconnectée de toutes les autres branches et commits.

L'index et l'arbre de travail sont ajustés comme si vous aviez précédemment exécuté git checkout <start_point> . Cela vous permet de commencer un nouvel historique qui enregistre un ensemble de chemins similaires à <start_point> par facilement git commit -a pour faire le commit Root.

Cela peut être utile lorsque vous souhaitez publier l'arbre d'un commit sans exposer son historique complet. Vous pouvez le faire pour publier une branche open source d'un projet dont l'arborescence actuelle est "propre", mais dont l'historique complet contient des morceaux de code propriétaires ou autrement de code propriétaire ou encombré.

Si vous voulez commencer un historique déconnecté qui enregistre un ensemble de chemins qui est totalement différent de celui de <start_point> alors vous devez effacer l'index et l'arbre de travail juste après avoir créé la branche orpheline en exécutant git rm -rf . du niveau supérieur de l'arbre de l'arbre de travail. Ensuite, vous serez prêt à préparer vos nouveaux fichiers, en repeuplant l'arbre de travail, en les copiant d'ailleurs, en extrayant une archive, etc.

Le lien de VonC vers les commentaires de Junio est intéressant. Je pense que le manuel devrait fournir des conseils dans ce cas, et permettre la bonne commande [ex. clone <branch> --options ] pour extraire uniquement la partie pertinente du repo. De toute évidence, la probabilité de push Le succès est accru par la présence de quelques commits liés et de SHA1s au bas de l'historique qui verrouilleront la correspondance du repo.


Mise à jour de Git 1.9.0 : notes de mise à jour 14 février 14.

"Récupérer d'un dépôt cloné peu profond était interdit, principalement parce que les chemins de code impliqués n'étaient pas soigneusement vérifiés et nous n'avons pas pris la peine de supporter un tel usage. Cette version tente d'autoriser le transfert d'objets à partir d'un dépôt cloné peu profond de manière d'une manière plus contrôlée (c'est-à-dire que le récepteur devient un référentiel peu profond avec un historique tronqué).".

C'est une bonne nouvelle pour les cloneurs peu profonds. Suivant - Clones étroits éventuellement.

12 votes

"réduire la confusion qu'un gros repo apporterait à un codeur naïf" Je pense que vous avez besoin de nouveaux développeurs :)

3 votes

Ces nouveaux développeurs commencent comme des codeurs naïfs ;-) Et une partie de cette confusion consiste à s'habituer à git lui-même, ce qui peut être un défi, donc nous allons commencer simplement...

4 votes

L'idée d'avoir une liste de commits est probablement le concept le plus fondamental de Git. Si j'avais été introduit à Git avec des dépôts qui ne contenaient toujours qu'un seul commit, je pense que j'aurais été encore plus confus.

22voto

VonC Points 414372

Comme Junio C. Hamano (mainteneur principal de Git) le met :

La règle n'est-elle pas plus ou moins la suivante :

Si l'historique de votre dépôt peu profond n'est pas assez long et que l'autre dépôt a bifurqué avant votre historique tronqué, vous ne pouvez pas calculer l'ancêtre commun et vous ne pouvez pas pousser.

Mise à jour 2014 : voir " Est-ce que git clone --depth 1 (clone superficiel) est plus utile qu'il ne le laisse croire ? " : cette limitation sera levée avec Git 1.9 !

Mise à jour 2015 : avec Git 2.5+, vous pourrez même Récupérer un seul commit . Voir " Extraire un commit spécifique d'un dépôt git distant "


Réponse originale (août 2011) :

En fait, en y réfléchissant, c'est beaucoup plus fort que "ne peut pas calculer le commun".

L'histoire peut ressembler à ceci :

          R---R---R
         /
  --R---R---X---X---S---S---S

S sont les commits que vous avez dans votre dépôt shallow, et R sont les commits qui existent dans le dépôt qui reçoit votre push.
Parce que votre histoire est superficielle, aucun des deux dépôts n'a ' X Ce sont les commits qui doivent exister afin de garder l'historique du dépôt du destinataire complet ; le destinataire n'est pas superficiel pour commencer, et nous ne voulons pas le rendre superficiel.

Si vous avez cloné superficiellement il y a quelque temps, vous avez travaillé sans communiquer avec l'autre côté pendant que l'autre côté progressait, ET si la progression de l'autre partie incluait un rembobinage et une reconstruction de l'histoire, vous verriez une topologie similaire.
La partie la plus à gauche ' S ' dans l'image ci-dessus pourrait avoir été l'extrémité de la branche lorsque vous avez cloné superficiellement avec la profondeur 1, et depuis lors, l'extrémité distante pourrait avoir écarté les trois commits les plus importants et avoir reconstruit son historique qui mène au commit le plus à droite R '.
Dans ce cas, il faut pousser jusqu'à la télécommande. HEAD échouera.


Donc, il pourrait fonctionne dans certains cas, mais elle n'est pas prise en charge :

Si je dois dire quelque chose à ce sujet...

  • Je pense que "n'est pas pris en charge" est un moyen succinct de donner une information suffisamment bonne, mais cela ne fonctionnerait que pour les personnes intelligentes.

  • Tout le monde n'est pas intelligent ; certains essaient eux-mêmes, voient que l'opération semble pour leur nombre limité d'essais, et en concluraient que cela fonctionnerait la plupart du temps.
    Et ils félicitent leur propre intelligence d'avoir dit "la plupart du temps", et non "toujours".
    Et ils s'énervent quand ils voient que ça ne marche pas, alors qu'ils ont été prévenus.


Pour plus d'informations sur le processus de mise à jour du clone superficiel, voir " Comment mettre à jour un clone git shallow ? ".

0 votes

De plus, le gain avec un clone peu profond n'est pas si grand pour commencer : blogs.gnome.org/simos/2009/04/18/

0 votes

Merci pour la référence à Nabble. Je ne peux pas accéder au blog depuis mon pare-feu actuel, je regarderai donc cela plus tard. Dans notre cas, nous savons que le R-R-R rejoindrait le S-S-S parce que nous l'avons configuré de cette façon. Il s'agit de trouver ce qui fonctionnera [le cas échéant] et ses conditions préalables.

0 votes

Ah, oui, j'avais vu ce post en faisant quelques recherches. N'est-ce pas une des lois de Linus que les fichiers grandissent, donc les deltas de paquets en sens inverse ? De plus, si j'ai bien compris, un fichier de base clone --depth 1 obtiendrait toutes les branches, chacune à la profondeur 1, plutôt qu'une seule branche spécifique à la profondeur 1. Ainsi, l'exemple du blog capterait davantage.

10voto

Karl Bielefeldt Points 15469

Existe-t-il un flux de travail git qui permet cela ?

Oui, c'est pour envoyer des corrections sous forme de patchs. git format-patch est spécialement conçu pour permettre cela. Il s'agit d'un flux de travail "gatekeeper", si vous voulez chercher plus de détails sur Google. Il est difficile de croire qu'une organisation aussi soucieuse de la "sécurité et de la protection de la propriété intellectuelle" que la vôtre n'utilise pas déjà quelque chose de similaire, où une personne ou un petit groupe est chargé de vérifier les modifications "non fiables" avant qu'elles ne soient intégrées à la version réelle.


Sur la base de votre commentaire, j'ai maintenant une meilleure idée de vos besoins. Ce que je vous recommande, c'est de créer un orphelin (voir git checkout --orphan ), à partir de n'importe quel point où vous voulez que vos développeurs commencent. Clonez uniquement cette branche vers un dépôt différent accessible à ces développeurs, et laissez-les cloner, pousser et tirer normalement depuis ce dépôt.

Ensuite, lorsque vous avez besoin de réintégrer leurs changements dans le dépôt officiel protégé, il suffit de tirer leur branche, d'en faire une copie avec le bouton git branch afin de ne pas écraser votre orphelin original (au cas où vous voudriez répéter le processus plus tard), puis rebasez la copie sur votre point de branchement original, et fusionnez ou autre comme d'habitude. L'historique aura l'air d'avoir été réalisé directement à partir de votre dépôt protégé.

C'est un peu plus compliqué que la normale, mais c'est le prix à payer pour une isolation supplémentaire.

1 votes

Le problème n'est pas leurs changements, c'est ce qu'il y a dans toutes les autres branches et l'histoire profonde, plus nous devons atténuer les "préoccupations" de la direction. De toute façon, ils ne feraient que repousser vers leur propre branche de développement, et il faut que cela ait l'air " intégré ". Donc beaucoup de priorités contradictoires !

0 votes

Le site orphan branch Cette option a l'air intéressante. Je vais y jeter un coup d'œil pour voir les avantages et les inconvénients par rapport à nos problèmes d'organisation locaux.

1 votes

Je ne pense pas que vous puissiez facilement rebaser les changements d'une branche orpheline dans votre branche master, car il n'y a pas de commit commun à rechercher.

1voto

J'ai trouvé une solution de contournement en utilisant les bundles git.

Cette solution répliquera exactement les mêmes commits vers l'autre dépôt comme le ferait "git push", et ne nécessitera pas de rebasement ou ne résultera pas en un changement d'ID de commit.

Malheureusement, cela nécessite un accès shell (tel que ssh) à l'hôte cible.

Je vais montrer la solution par l'exemple.

Tout d'abord, nous devons obtenir un clone peu profond à des fins de démonstration.

Récupérons la version unique v0.5.0.0 à partir de https://github.com/skarnet/s6-rc dans un nouveau dépôt comme un clone superficiel.

J'utiliserai des variables de l'interpréteur de commandes dans mes exemples plutôt que d'inclure les paramètres de l'exemple directement dans les commandes, car cela vous permettra de copier/coller les instructions de ce message directement dans votre interpréteur de commandes, après avoir défini les variables à des valeurs différentes qui s'appliquent à votre situation.

Par conséquent, n'hésitez pas à remplacer les affectations de variables suivantes en utilisant une URL et une version différentes.

Dans le cas de notre exemple, le clone peu profond peut être créé avec :

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ git pull --depth=1 "$url" $rel:master

Cela créera un sous-répertoire "s6-src" (en utilisant les valeurs des variables ci-dessus) contenant le nouveau clone.

Maintenant que nous avons notre clone peu profond contenant seulement un seul commit avec tout son historique parent manquant dans le dépôt local, nous regroupons ce seul commit dans un fichier bundle git :

$ b=$rel.gbnd
$ git bundle create $b HEAD

Ceci créera le fichier v0.5.0.0.gbnd en utilisant les variables shell définies précédemment.

Maintenant, vous devez copier ce fichier sur la machine cible vers laquelle vous voudriez normalement pousser. (Seulement, git push refuse de pousser à partir de clones superficiels et ne fonctionnera donc pas, du moins pas en utilisant les anciennes versions de git).

Sur l'hôte cible, faites ce qui suit afin de créer un nouveau dépôt en tant que sous-répertoire, contenant le même commit que celui regroupé auparavant :

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ git init ${url##*/} && cd ${url##*/}
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c # optional: create a tag for the imported commit.
$ git reset --hard $c
$ git fetch --depth=1 .

Notez que vous devez définir les variables avec les mêmes valeurs que sur l'hôte à partir duquel le paquet a été copié.

Notez également que vous pouvez omettre le "git init" si le dépôt existe déjà.

C'est ça !

Toutefois, ces dernières instructions ne s'appliquent qu'aux caisses normales.

Peut-être que vous voulez importer un paquet de clones superficiels dans un dépôt "nu".

Dans ce cas, faites plutôt ce qui suit :

$ url=https://github.com/skarnet/s6-rc
$ rel=v0.5.0.0
$ cd ${url##*/}.git
$ c=`git bundle unbundle $b | cut -d " " -f 1`; echo "$c"
$ git tag $rel $c
$ git fetch --depth=1 . $rel

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X