Récupérer un commit spécifique à partir d'un référentiel Git distant

194

Existe-t-il un moyen de récupérer un seul commit spécifique à partir d'un dépôt Git distant sans le cloner sur mon PC? La structure du dépôt distant est absolument la même que celle du mien et il n'y aura donc aucun conflit, mais je ne sais pas comment faire cela et je ne veux pas cloner cet énorme dépôt.

Je suis nouveau dans git, existe-t-il un moyen

Varun Chitre
la source
1
Votre dépôt existant est-il déjà un clone du dépôt distant ou est-il complètement différent?
CharlesB
Eh bien, le dépôt est la source du noyau Linux, et c'est à peu près le même
Varun Chitre
alors est-ce un clone ou non?
CharlesB
1
Pas exactement. Considérez ceci, laissez le repo distant être à la tête D et le mien est à la tête A et est derrière par B, C, D commits. Je souhaite fusionner le commit B d'un repo et C d'un autre et D d'un autre car les commits B, C, D dans ces dépôts sont différents avec leurs propres spécialités
Varun Chitre
1
@VarunChitre pouvez-vous accepter l'autre réponse de VonC?
CharlesB

Réponses:

110

À partir de Git version 2.5+ (Q2 2015), récupérer un seul commit (sans cloner le dépôt complet) est en fait possible.

Voir commit 68ee628 par Fredrik Medley ( moroten) , 21 mai 2015
(fusionné par Junio ​​C Hamano - gitster- dans commit a9d3493 , 01 juin 2015)

Vous avez maintenant une nouvelle configuration (côté serveur)

uploadpack.allowReachableSHA1InWant

Autorise upload-packà accepter une demande de récupération qui demande un objet accessible depuis n'importe quel conseil de référence. Cependant, notez que le calcul de l'accessibilité des objets est coûteux en calcul.
La valeur par défaut est false.

Si vous combinez cette configuration côté serveur avec un clone superficiel ( git fetch --depth=1), vous pouvez demander un seul commit (voir t/t5516-fetch-push.sh:

git fetch --depth=1 ../testrepo/.git $SHA1

Vous pouvez utiliser la git cat-filecommande pour voir que le commit a été récupéré:

git cat-file commit $SHA1

" git upload-pack" qui sert " git fetch" peut être dit de servir des commits qui ne sont à la pointe d'aucune référence, tant qu'ils sont accessibles depuis une référence, avec uploadpack.allowReachableSHA1InWant une variable de configuration.


La documentation complète est:

upload-pack: autorise éventuellement la récupération de sha1 accessible

Avec l' uploadpack.allowReachableSHA1InWantoption de configuration définie côté serveur, " git fetch" peut faire une requête avec une ligne "want" qui nomme un objet qui n'a pas été annoncé (susceptible d'avoir été obtenu hors bande ou à partir d'un pointeur de sous-module).
Seuls les objets accessibles depuis les extrémités des branches, c'est-à-dire l'union des branches annoncées et des branches masquées par transfer.hideRefs, seront traités.
Notez qu'il y a un coût associé à devoir revenir en arrière dans l'historique pour vérifier l'accessibilité.

Cette fonctionnalité peut être utilisée lors de l'obtention du contenu d'un certain commit, pour lequel le sha1 est connu, sans avoir besoin de cloner tout le référentiel, surtout si une extraction superficielle est utilisée .

Les cas utiles sont par exemple

  • référentiels contenant des fichiers volumineux dans l'historique,
  • récupérer uniquement les données nécessaires pour une extraction de sous-module,
  • lors du partage d'un sha1 sans dire à quelle branche exacte il appartient et dans Gerrit, si vous pensez en termes de commits au lieu de changements de nombres.
    (Le cas Gerrit a déjà été résolu allowTipSHA1InWantcar chaque changement de Gerrit a une référence.)

Git 2.6 (Q3 2015) améliorera ce modèle.
Voir commit 2bc31d1 , commit cc118a6 (28 juillet 2015) par Jeff King ( peff) .
(Fusionné par Junio ​​C Hamano - gitster- in commit 824a0be , 19 août 2015)

refs: support négatif transfer.hideRefs

Si vous masquez une hiérarchie de références à l'aide de la transfer.hideRefsconfiguration, il n'y a aucun moyen de surcharger ultérieurement cette configuration pour la "montrer".
Ce patch implémente un masquage "négatif" qui fait que les correspondances sont immédiatement marquées comme non masquées, même si une autre correspondance la masquerait.
Nous prenons soin d'appliquer les correspondances dans l'ordre inverse de la façon dont elles nous sont fournies par la machine de configuration, car cela permet à notre habituelle priorité de configuration "le dernier gagne" (et les entrées dans .git/config, par exemple, seront remplacées /etc/gitconfig).

Vous pouvez donc maintenant faire:

git config --system transfer.hideRefs refs/secret
git config transfer.hideRefs '!refs/secret/not-so-secret'

pour se cacher refs/secretdans tous les dépôts, à l'exception d'un bit public dans un dépôt spécifique.


Git 2.7 (novembre / décembre 2015) s'améliorera à nouveau:

Voir commit 948bfa2 , commit 00b293e (05 novembre 2015), commit 78a766a , commit 92cab49 , commit 92cab49 , commit 92cab49 (03 nov 2015), commit 00b293e , commit 00b293e (05 nov 2015) et commit 92cab49 , commit 92cab49 , commit 92cab49 , commit 92cab49 (03 novembre 2015) par Lukas Fleischer ( lfos) .
Aide: Eric Sunshine ( sunshineco) .
(Fusionné par Jeff King - peff- dans commit dbba85e , 20 novembre 2015)

config.txt: documenter la sémantique de hideRefswith namespaces

À l'heure actuelle, il n'y a pas de définition claire du transfer.hideRefscomportement à adopter lorsqu'un espace de noms est défini.
Expliquez que les hideRefspréfixes correspondent aux noms supprimés dans ce cas. C'est ainsi que les hideRefsmodèles sont actuellement traités dans receive-pack.

hideRefs: ajout de la prise en charge de la correspondance des références complètes

En plus de faire correspondre les références dépouillées, on peut maintenant ajouter des hideRefsmodèles avec lesquels la référence complète (non rayée) est comparée.
Pour faire la distinction entre les correspondances dénudées et complètes, ces nouveaux modèles doivent être précédés d'un circumflex ( ^).

D'où la nouvelle documentation :

transfer.hideRefs:

Si un espace de noms est en cours d'utilisation, le préfixe d'espace de noms est supprimé de chaque référence avant d'être mis en correspondance avec les transfer.hiderefsmodèles.
Par exemple, si refs/heads/masterest spécifié dans transfer.hideRefset que l'espace de noms actuel l'est foo, alors refs/namespaces/foo/refs/heads/master est omis des publicités mais refs/heads/masteret refs/namespaces/bar/refs/heads/masterest toujours annoncé comme des lignes dites "ont".
Afin de faire correspondre les références avant le décapage, ajoutez un ^devant le nom de la référence. Si vous combinez !et ^, !doit être spécifié en premier.


R .. mentionne dans les commentaires la configuration uploadpack.allowAnySHA1InWant, qui permet upload-packd'accepter une fetchrequête qui demande n'importe quel objet. (Valeur par défaut false).

Voir commit f8edeaa (nov. 2016, Git v2.11.1) par David "novalis" Turner ( novalis) :

upload-pack: autorise éventuellement la récupération de tout sha1

Il semble un peu idiot de faire un contrôle d'accessibilité dans le cas où nous faisons confiance à l'utilisateur pour accéder à absolument tout dans le référentiel.

En outre, c'est racé dans un système distribué - peut-être qu'un serveur annonce une référence, mais un autre a depuis eu une poussée forcée vers cette référence, et peut-être que les deux requêtes HTTP finissent par être dirigées vers ces différents serveurs.

VonC
la source
4
Pouvez-vous donner un exemple plus complet sur la façon de créer un clone de dépôt avec juste ce seul commit? J'ai essayé mais j'ai échoué .. Merci!
Lars Bilke
1
Je veux pousser vers GitHub. Peut-être qu'ils ne le permettent pas.
Lars Bilke
2
@LarsBilke nous parlons de cloner ou de tirer ici, pas de pousser. Et je suis sûr que GitHub n'a pas encore Git 2.5 côté serveur.
VonC
2
Maintenant encore mieux, il n'y a uploadpack.allowAnySHA1InWantpas de pénalité de calcul d'accessibilité (et de vecteur DoS).
R .. GitHub STOP HELPING ICE
1
Merci! Je trouve drôle qu'ils le décrivent comme "faire confiance à l'utilisateur pour accéder" plutôt que "faire confiance aux auteurs du repo pour ne pas pousser des conneries aléatoires qu'ils n'ont pas l'intention de rendre public".
R .. GitHub STOP HELPING ICE
100

Vous ne clonez qu'une seule fois, donc si vous avez déjà un clone du référentiel distant, en extraire ne téléchargera pas tout à nouveau. Indiquez simplement la branche que vous souhaitez extraire ou récupérez les modifications et récupérez le commit souhaité.

Récupérer à partir d'un nouveau référentiel est très économique en bande passante, car il ne téléchargera que les modifications que vous n'avez pas. Pensez à ce que Git fasse la bonne chose, avec une charge minimale.

Git stocke tout dans un .gitdossier. Un commit ne peut pas être récupéré et stocké isolément, il a besoin de tous ses ancêtres. Ils sont interdépendants .


Pour réduire la taille du téléchargement, vous pouvez cependant demander à git de récupérer uniquement les objets liés à une branche ou un commit spécifique:

git fetch origin refs/heads/branch:refs/remotes/origin/branch

Cela téléchargera uniquement les commits contenus dans la branche distante branch (et uniquement ceux que vous manquez) et les stockera dans origin/branch. Vous pouvez ensuite fusionner ou commander.

Vous pouvez également spécifier uniquement un commit SHA1:

git fetch origin 96de5297df870:refs/remotes/origin/foo-commit

Cela téléchargera uniquement le commit du SHA-1 96de5297df870 spécifié (et ses ancêtres que vous manquez) et le stockera en tant que branche distante (non existante) origin/foo-commit.

CharlesB
la source
3
On dirait que vous faites une confusion sur ce que signifie clone. Lorsque vous récupérez des modifications à partir d'un dépôt distant, vous ne le clonez pas, vous obtenez simplement les validations dans votre historique. Ensuite, vous choisissez le commit que vous souhaitez extraire ou le fusionnez dans votre historique
CharlesB
1
Il télécharge toujours beaucoup de données (430 Mo) avec git fetch. Le commit requis est juste de quelques kbs. Il n'y a pas de commande spéciale pour faire cela vraiment? Et si je veux supprimer le repo 'git fetched'? où est-il stocké?
Varun Chitre le
9
C'est plutôt dépassé maintenant. Nous avons à la fois la possibilité d'effectuer un clone superficiel , ainsi que de récupérer un seul commit . Les clones peu profonds sont désormais autorisés à pousser et à récupérer normalement, sans avoir à connaître l'historique complet du projet, il n'est donc plus correct de dire qu'un commit ne peut pas exister seul sans ses ancêtres. Ce que vous dites à propos de la récupération après le clone initial est très vrai, mais nous avons également des options encore moins chères.
Theodore Murdock
6
La dernière commande (utilisant SHA1 commit) ne fonctionne pas pour moi. La commande fait "quelque chose" en silence pendant un moment, puis se termine sans message ni effet secondaire apparent.
HRJ
1
@HRJ Oui, j'ai rencontré ça aussi, sur Ubuntu 16.04 avec Git 2.7.4-0ubuntu1.3. Cependant, lors de l'utilisation 2.16.2-0ppa1~ubuntu16.04.1depuis le PPA git-core, cela fonctionne comme il se doit. Sonne comme un bug qui a été corrigé. Impossible de trouver une référence à cela avec une recherche rapide. Si quelqu'un peut me donner un indice à ce sujet, j'aimerais que ce correctif soit rétroporté.
gertvdijk
64

J'ai fait un tirage sur mon repo git:

git pull --rebase <repo> <branch>

Permettre à git de récupérer tout le code de la branche, puis je suis allé faire une réinitialisation du commit qui m'intéressait.

git reset --hard <commit-hash>

J'espère que cela t'aides.

Piu Sharma
la source
1
Aucune des réponses n'a fonctionné, mais celle-ci m'a sauvé la vie! Merci beaucoup!
michaeltintiuc
La réinitialisation --hard a fonctionné pour moi après le clonage! Merci.
Nick-ACNB
3
-1: les commandes «destructives» comme git reset --hard, lorsqu'elles sont partagées dans des solutions généralisées, peuvent conduire les gens dans des pièges où ils perdent des données (ou, dans ce cas: dans un état où la récupération de leurs données n'est pas triviale).
Ry Biesemeyer
57

Vous pouvez simplement récupérer un seul commit d'un dépôt distant avec

git fetch <repo> <commit>

où,

  • <repo>peut être un nom de dépôt distant (par exemple origin) ou même une URL de dépôt distant (par exemple https://git.foo.com/myrepo.git)
  • <commit> peut être le commit SHA1

par exemple

git fetch https://git.foo.com/myrepo.git 0a071603d87e0b89738599c160583a19a6d95545

après avoir récupéré le commit (et les ancêtres manquants), vous pouvez simplement le récupérer avec

git checkout FETCH_HEAD

Notez que cela vous amènera à l'état de «tête détachée».

Couler
la source
11
Quand j'essaye fetchun rev spécifique comme vous le faites là, git échoue avec le code d'erreur 1 et aucune sortie. Est-ce quelque chose qui fonctionnait dans les versions précédentes? (Je suis v2.0.2.)
Jack O'Connor
2
Edit: Cela fonctionne si j'ai déjà le commit that localement, comme si j'ai déjà fait un full fetch, bien que dans ce cas je ne sais pas quelle est l'utilisation.
Jack O'Connor
3
En effet, cela ne semble plus fonctionner pour moi non plus avec git 2.0.2. :(
Débit
2
git checkout FETCH_HEADaide.
lzl124631x
2
Cette méthode ne fonctionnera pas avec l'extraction superficielle (par exemple --depth=1)!
kingmakerking
16

Vous pouvez simplement récupérer le dépôt distant avec:

git fetch <repo>

où,

  • <repo>peut être un nom de dépôt distant (par exemple origin) ou même une URL de dépôt distant (par exemple https://git.foo.com/myrepo.git)

par exemple:

git fetch https://git.foo.com/myrepo.git 

après avoir récupéré les dépôts, vous pouvez fusionner les commits que vous voulez (puisque la question concerne la récupération d'un commit, à la place, vous pouvez utiliser cherry-pick pour choisir un seul commit):

git merge <commit>
  • <commit> peut être le commit SHA1

par exemple:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

ou

git merge 0a071603d87e0b89738599c160583a19a6d95545

s'il s'agit du dernier commit que vous souhaitez fusionner, vous pouvez également utiliser la variable FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD
Sérgio
la source
Cela nécessite la configuration d'un compte Git sur une machine. Cela ne fonctionne pas sous un compte de test. Avez-vous quelque chose qui fonctionne sous un compte de test?
jww
ce que vous voulez dire ? vous ne pouvez pas faire git chercher?
Sérgio
Ummm alors la commande serait git config set uploadpack.allowReachableSHA1InWant ?
Alexander Mills
3

Cela fonctionne mieux:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

nom "temp" comme vous voulez ... cette branche est peut-être orpheline

Alexander Mills
la source
Clairement PAS avec les anciennes versions de git comme 1.8.x
sorin
1

Enfin, j'ai trouvé un moyen de cloner un commit spécifique en utilisant git cherry-pick . En supposant que vous n'avez pas de référentiel en local et que vous extrayez un commit spécifique à distance,

1) créer un référentiel vide dans local et git init

2) git remote add origin " url-of-repository "

3) git fetch origin [cela ne déplacera pas vos fichiers vers votre espace de travail local à moins que vous ne les fusionniez]

4) git cherry-pick " Entrez-long-commit-hash-that-you-need "

De cette façon, vous n'aurez que les fichiers de ce commit spécifique dans votre fichier local.

Entrez-long-commit-hash:

Vous pouvez l'obtenir en utilisant -> git log --pretty = oneline

Surya Deepak
la source
0

Si le commit demandé se trouve dans les pull requests du référentiel distant, vous pouvez l'obtenir par son ID:

# Add the remote repo path, let's call it 'upstream':
git remote add upstream https://github.com/repo/project.git

# checkout the pull ID, for example ID '60':
git fetch upstream pull/60/head && git checkout FETCH_HEAD
Noam Manos
la source