Recupera commit specifici da un repository Git remoto


189

Esiste un modo per recuperare solo un commit specifico da un repository Git remoto senza clonarlo sul mio PC? La struttura del repository remoto è assolutamente uguale alla mia e quindi non ci saranno conflitti ma non ho idea di come farlo e non voglio clonare quell'enorme repository.

Sono nuovo di Git, c'è qualche modo?


1
Il tuo repository esistente è già un clone di quello remoto o è completamente diverso?
Charles B

Bene, il repository è il sorgente del kernel Linux, ed è praticamente lo stesso
Varun Chitre,

quindi è un clone o no?
Charles B

1
Non esattamente. Considera questo, Lascia che il repository remoto sia nella testa D e il mio è nella testa A ed è dietro di B, C, D. Vorrei unire il commit B da un repository e C da un altro e D da un altro poiché i commit B, C, D in questi repository sono diversi con le loro specialità
Varun Chitre,

1
@VarunChitre puoi accettare l'altra risposta di VonC?
Charles B

Risposte:


109

A partire da Git versione 2.5+ (Q2 2015), è effettivamente possibile recuperare un singolo commit (senza clonare l'intero repository).

Vedi commit 68ee628 di Fredrik Medley ( moroten) , 21 maggio 2015.
(Fusione di Junio ​​C Hamano - gitster- in commit a9d3493 , 01 giu 2015)

Ora hai una nuova configurazione (sul lato server)

uploadpack.allowReachableSHA1InWant

Consentire upload-packdi accettare una richiesta di recupero che richiede un oggetto raggiungibile da qualsiasi suggerimento di riferimento. Tuttavia, si noti che il calcolo della raggiungibilità degli oggetti è costoso dal punto di vista computazionale.
L'impostazione predefinita è false.

Se si combina quella configurazione lato server con un clone superficiale ( git fetch --depth=1), è possibile richiedere un singolo commit (consultare t/t5516-fetch-push.sh:

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

Puoi usare il git cat-filecomando per vedere che il commit è stato recuperato:

git cat-file commit $SHA1

" git upload-pack" che serve " git fetch" può essere detto di servire i commit che non sono sulla punta di alcun riferimento, purché siano raggiungibili da un riferimento, con uploadpack.allowReachableSHA1InWant variabile di configurazione.


La documentazione completa è:

upload-pack: facoltativamente consenti il ​​recupero di sha1 raggiungibile

Con l' uploadpack.allowReachableSHA1InWantopzione di configurazione impostata sul lato server, " git fetch" può effettuare una richiesta con una riga "want" che denomina un oggetto che non è stato pubblicizzato (probabilmente è stato ottenuto fuori banda o da un puntatore sottomodulo). Verranno elaborati
solo gli oggetti raggiungibili dalle punte dei rami, ovvero l'unione dei rami pubblicizzati e dei rami nascosti da transfer.hideRefs.
Si noti che esiste un costo associato di dover tornare indietro nella cronologia per verificare la raggiungibilità.

Questa funzione può essere utilizzata quando si ottiene il contenuto di un determinato commit, per il quale è noto sha1, senza la necessità di clonare l'intero repository, specialmente se viene utilizzato un recupero superficiale .

Casi utili sono ad es

  • repository contenenti file di grandi dimensioni nella cronologia,
  • recuperare solo i dati necessari per un checkout del sottomodulo,
  • quando condividi uno sha1 senza dire a quale ramo esatto appartiene e in Gerrit, se pensi in termini di commit invece di cambiare i numeri.
    (Il caso Gerrit è già stato risolto allowTipSHA1InWantpoiché ogni modifica di Gerrit ha un riferimento.)

Git 2.6 (Q3 2015) migliorerà quel modello.
Vedi commit 2bc31d1 , commit cc118a6 (28 lug 2015) di Jeff King ( peff) .
(Unita da Junio ​​C Hamano - gitster- in commit 824a0be , 19 ago 2015)

refs: supporto negativo transfer.hideRefs

Se nascondi una gerarchia di ref usando la transfer.hideRefsconfigurazione, non c'è modo di sostituire successivamente quella configurazione per "scoprirla".
Questa patch implementa un nascondiglio "negativo" che fa sì che le corrispondenze vengano immediatamente contrassegnate come nascoste, anche se un'altra corrispondenza lo nasconderebbe.
Ci preoccupiamo di applicare le partite in ordine inverso rispetto a come ci vengono fornite dal macchinario di configurazione, in quanto ciò consente al nostro solito lavoro di precedenza di configurazione "ultimo vincitore" (e le voci .git/config, ad esempio, avranno la precedenza /etc/gitconfig).

Quindi ora puoi fare:

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

per nascondere refs/secretin tutti i repository, ad eccezione di un bit pubblico in un repository specifico.


Git 2.7 (nov / dic 2015) migliorerà di nuovo:

Vedi commit 948bfa2 , commit 00b293e (05 nov 2015), commit 78a766a , commit 92cab49 , commit 92cab49 , commit 92cab49 (03 nov 2015), commit 00b293e , commit 00b293e (05 nov 2015) e commit 92cab49 , commit 92cab49 , commit 92cab49 , commit 92cab49 (03 nov 2015) di Lukas Fleischer ( lfos) .
Aiutato da: Eric Sunshine ( sunshineco) .
(Unito da Jeff King - peff- in commit dbba85e , 20 nov 2015)

config.txt: documenta la semantica di hideRefscon namespace

Al momento, non esiste una definizione chiara di come transfer.hideRefsdovrebbe comportarsi quando viene impostato uno spazio dei nomi.
Spiega che i hideRefsprefissi corrispondono ai nomi eliminati in quel caso. Questo è il modo in cui i hideRefsmodelli sono attualmente gestiti nel pacchetto di ricezione.

hideRefs: aggiunge il supporto per la corrispondenza dei riferimenti completi

Oltre ad abbinare i ref stripped, ora è possibile aggiungere dei hideRefspattern a cui è confrontato il ref completo (non strippato).
Per distinguere tra corrispondenze spogliate e intere, questi nuovi schemi devono essere preceduti da una circonflessa ( ^).

Da qui la nuova documentazione :

transfer.hideRefs:

Se viene utilizzato uno spazio dei nomi, il prefisso dello spazio dei nomi viene rimosso da ogni riferimento prima che venga confrontato con i transfer.hiderefsmodelli.
Ad esempio, se refs/heads/masterviene specificato in transfer.hideRefse lo spazio dei nomi corrente lo è foo, refs/namespaces/foo/refs/heads/master viene omesso dagli annunci pubblicitari refs/heads/mastere refs/namespaces/bar/refs/heads/masterviene comunque pubblicizzato come le cosiddette righe "have".
Per abbinare i ref prima dello stripping, aggiungi un ^davanti al nome dell'arbitro. Se si combinano !e ^, è !necessario specificare prima.


R .. menziona nei commenti la configurazione uploadpack.allowAnySHA1InWant, che consente upload-packdi accettare una fetchrichiesta che richiede qualsiasi oggetto. (L'impostazione predefinita è false).

Vedi commit f8edeaa (novembre 2016, Git v2.11.1) di David "novalis" Turner ( novalis) :

upload-pack: opzionalmente consentire il recupero di qualsiasi sha1

Sembra un po 'sciocco fare un controllo di raggiungibilità nel caso in cui ci fidiamo che l'utente acceda assolutamente a tutto nel repository.

Inoltre, è audace in un sistema distribuito - forse un server pubblicizza un riferimento, ma da allora un altro ha avuto una spinta forzata verso quel riferimento, e forse le due richieste HTTP finiscono per essere dirette a questi diversi server.


4
Puoi fare un esempio più completo su come creare un clone repo con questo singolo commit? Ho provato ma fallito .. Grazie!
Lars Bilke,

1
Voglio spingere su GitHub. Forse non lo consentono.
Lars Bilke,

2
@LarsBilke stiamo parlando di clone o tirare qui, non spingere. E sono abbastanza sicuro che GitHub non abbia ancora Git 2.5 sul lato server.
VonC,

2
Ora ancora meglio, c'è uploadpack.allowAnySHA1InWantsenza la penalità di calcolo di raggiungibilità (e vettore DoS).
R .. GitHub smette di aiutare ICE il

1
Grazie! Trovo divertente che lo descrivano come "fidati che l'utente acceda" piuttosto che "fidati degli autori del repository di non spingere cazzate casuali che non intendono rendere pubbliche".
R .. GitHub FERMA AIUTANDO ICE il

98

Cloni solo una volta, quindi se hai già un clone del repository remoto, estrarre da esso non scaricherà di nuovo tutto. Indica semplicemente quale ramo vuoi estrarre o recupera le modifiche e verifica il commit desiderato.

Il recupero da un nuovo repository è molto economico in termini di larghezza di banda, in quanto scaricherà solo le modifiche che non hai. Pensa in termini di Git che fa la cosa giusta, con un carico minimo.

Git memorizza tutto nella .gitcartella. Un commit non può essere recuperato e archiviato in modo isolato, ha bisogno di tutti i suoi antenati. Sono correlati .


Per ridurre le dimensioni del download puoi comunque chiedere a git di recuperare solo oggetti relativi a un ramo specifico o commit:

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

Questo scaricherà solo i commit contenuti nella filiale remota branch (e solo quelli che ti mancano) e li memorizzerà origin/branch. È quindi possibile unire o effettuare il checkout.

Puoi anche specificare solo un commit SHA1:

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

Questo scaricherà solo il commit dello specifico SHA-1 96de5297df870 (e dei suoi antenati che ti mancano) e lo memorizzerà come ramo remoto (inesistente) origin/foo-commit.


3
Sembra che tu stia facendo confusione sul significato del clone. Quando recuperi le modifiche da un repository remoto non lo cloni, ricevi semplicemente i commit nella tua cronologia. Quindi scegli quale commit vuoi verificare o uniscilo nella tua cronologia
CharlesB

1
Scarica ancora molti dati (430mb) con git fetch. Il commit richiesto è solo di pochi kb. Non esiste un comando speciale per farlo davvero? E se volessi rimuovere il repository 'git fetched'? dove è memorizzato?
Varun Chitre,

9
Questo è piuttosto obsoleto ora. Abbiamo sia la capacità di eseguire un clone superficiale , sia di recuperare un singolo commit . I cloni poco profondi ora sono autorizzati a spingere e recuperare normalmente, senza dover conoscere l'intera storia del progetto, quindi non è più corretto affermare che un commit non può esistere da solo senza i suoi antenati. Quello che dici sul recupero dopo il clone iniziale è molto vero, ma abbiamo anche opzioni ancora più economiche.
Theodore Murdock,

6
L'ultimo comando (usando il commit SHA1) non funziona per me. Il comando fa "qualcosa" silenziosamente per un po ', quindi esce senza alcun messaggio o apparente effetto collaterale.
HRJ,

1
@HRJ Sì, l'ho riscontrato anche su Ubuntu 16.04 con Git 2.7.4-0ubuntu1.3. Tuttavia, quando si utilizza 2.16.2-0ppa1~ubuntu16.04.1dal PPA git-core, questo funziona come dovrebbe. Sembra un bug che è stato corretto. Impossibile trovare un riferimento a quello con una ricerca rapida. Se qualcuno può darmi un puntatore su questo, mi piacerebbe che questa correzione fosse ripristinata.
gertvdijk,

62

Ho fatto un tiro sul mio repository git:

git pull --rebase <repo> <branch>

Permettendo a git di inserire tutto il codice per il ramo e poi sono andato a fare un reset al commit che mi interessava.

git reset --hard <commit-hash>

Spero che questo ti aiuti.


1
Nessuna delle risposte ha funzionato, questa però mi ha salvato la vita! Grazie mille!
michaeltintiuc,

Il reset --hard ha funzionato per me dopo la clonazione! Grazie.
Nick-ACNB,

3
-1: comandi "distruttivi" come git reset --hard, se condivisi in soluzioni generalizzate, possono condurre le persone in trappole in cui perdono dati (o, in questo caso: in uno stato in cui il recupero dei loro dati non è banale).
yaauie

54

Puoi semplicemente recuperare un singolo commit di un repository remoto con

git fetch <repo> <commit>

dove,

  • <repo>può essere un nome repository remoto (ad es. origin) o anche un URL repository remoto (ad es. https://git.foo.com/myrepo.git)
  • <commit> può essere il commit SHA1

per esempio

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

dopo aver recuperato il commit (e gli antenati mancanti) puoi semplicemente verificarlo con

git checkout FETCH_HEAD

Nota che questo ti porterà nello stato "testa staccata".


10
Quando provo a fetchun giro specifico come fai lì, git fallisce con il codice di errore 1 e nessun output. Era qualcosa che funzionava nelle versioni precedenti? (Sono v2.0.2.)
Jack O'Connor il

2
Modifica: Funziona se ho già il commit a livello locale, come se avessi già fatto un pieno fetch, anche se in quel caso non sono sicuro di quale sia l'uso.
Jack O'Connor,

2
In effetti, questo non sembra funzionare più con me nemmeno con git 2.0.2. :(
Flusso

2
git checkout FETCH_HEADaiuta.
lzl124631x,

1
Questo metodo non funziona con il recupero superficiale (ad es. --depth=1)!
Kingmaker

16

Puoi semplicemente recuperare il repository remoto con:

git fetch <repo>

dove,

  • <repo>può essere un nome repository remoto (ad es. origin) o anche un URL repository remoto (ad es. https://git.foo.com/myrepo.git)

per esempio:

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

dopo aver recuperato i repository è possibile unire i commit desiderati (poiché la domanda riguarda il recupero di un commit, invece di unire è possibile utilizzare cherry-pick per selezionare un solo commit):

git merge <commit>
  • <commit> può essere il commit SHA1

per esempio:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

o

git merge 0a071603d87e0b89738599c160583a19a6d95545

se è l'ultimo commit che vuoi unire, puoi anche usare la variabile FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD

Ciò richiede una configurazione dell'account Git su una macchina. Non funziona con un account di prova. Hai qualcosa che funziona con un account di prova?
JWW

Cosa vuoi dire ? non puoi fare git fetch?
Sérgio,

Ummm quindi il comando sarebbe git config set uploadpack.allowReachableSHA1InWant ?
Alexander Mills,

2

Funziona meglio:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

nome "temp" come vuoi ... questo ramo potrebbe essere orfano però


Chiaramente NON con versioni precedenti di git come 1.8.x
sorin

1

Alla fine ho trovato un modo per clonare il commit specifico usando git cherry-pick . Supponendo che non si disponga di alcun repository in locale e si stia eseguendo il commit specifico da remoto,

1) creare un repository vuoto in local e git init

2) git remote aggiungi origine " url-of-repository "

3) git fetch origin [questo non sposterà i tuoi file nell'area di lavoro locale se non li unisci]

4) git cherry-pick " Inserisci-long-commit-hash-che-ti-serve "

Fatto. In questo modo, avrai solo i file di quel commit specifico nel tuo locale.

Inserisci lungo-commit-hash:

Puoi ottenerlo usando -> git log --pretty = oneline



0

Se il commit richiesto si trova nelle richieste pull del repository remoto, è possibile ottenerlo tramite il suo 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.