Recuperar confirmação específica de um repositório Git remoto


189

Existe alguma maneira de recuperar apenas um commit específico de um repositório Git remoto sem cloná-lo no meu PC? A estrutura do repositório remoto é absolutamente igual à minha e, portanto, não haverá conflitos, mas não tenho idéia de como fazer isso e não quero clonar esse enorme repositório.

Eu sou novo no git, existe alguma maneira?


1
Seu repo existente já é um clone do remoto, ou é completamente diferente?
precisa

Bem, a repo é fonte Linux kernel, e sua muito bonito mesmo
Varun Chitre

então é um clone ou não?
precisa

1
Não exatamente. Considere isso: deixe o repositório remoto estar na cabeça D e a minha estar na cabeça A e ficar atrás por confirmações de B, C e D. Desejo fundir cometer B de um repo e C do outro e D a partir de uma outra pessoa como a B, C, D commits nestes repos são diferentes com suas próprias especialidades
Varun Chitre

1
@VarunChitre, você pode aceitar a outra resposta do VonC?
charlesb

Respostas:


109

Começando com o Git versão 2.5+ (Q2 2015), é possível buscar um único commit (sem clonar o repositório completo).

Ver commit 68ee628 por Fredrik Medley ( moroten) , 21 de maio de 2015.
(mesclado por Junio ​​C Hamano - gitster- no commit a9d3493 , 01 jun 2015)

Agora você tem uma nova configuração (no lado do servidor)

uploadpack.allowReachableSHA1InWant

Permite upload-packaceitar uma solicitação de busca que solicita um objeto acessível a partir de qualquer dica de referência. No entanto, observe que calcular a acessibilidade de objetos é computacionalmente caro.
O padrão é false.

Se você combinar essa configuração do lado do servidor com um clone superficial ( git fetch --depth=1), poderá solicitar uma única confirmação (consulte t/t5516-fetch-push.sh:

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

Você pode usar o git cat-filecomando para verificar se o commit foi buscado:

git cat-file commit $SHA1

" git upload-pack" que serve " git fetch" pode ser dito para servir confirmações que não estão na ponta de qualquer referência, desde que sejam alcançáveis ​​a partir de uma referência, com a uploadpack.allowReachableSHA1InWant variável de configuração.


A documentação completa é:

upload-pack: permitir opcionalmente buscar sha1 acessível

Com a uploadpack.allowReachableSHA1InWantopção de configuração definida no lado do servidor, " git fetch" pode fazer uma solicitação com uma linha "querer" que nomeie um objeto que não foi anunciado (provavelmente obtido fora da banda ou de um ponteiro de submódulo).
Apenas objetos alcançáveis ​​a partir das dicas de ramificação, ou seja, a união de ramificações anunciadas e ramificações ocultas por transfer.hideRefs, serão processados.
Observe que há um custo associado de ter que retornar ao histórico para verificar a acessibilidade.

Esse recurso pode ser usado ao obter o conteúdo de um determinado commit, pelo qual o sha1 é conhecido, sem a necessidade de clonar todo o repositório, especialmente se uma busca superficial for usada .

Casos úteis são, por exemplo,

  • repositórios contendo arquivos grandes no histórico,
  • buscando apenas os dados necessários para uma finalização do submódulo,
  • ao compartilhar um sha1 sem dizer a qual ramo exato ele pertence e no Gerrit, se você pensa em termos de confirmação em vez de alterar números.
    (O caso Gerrit já foi resolvido, allowTipSHA1InWantpois todas as alterações da Gerrit têm uma ref.)

O Git 2.6 (terceiro trimestre de 2015) melhorará esse modelo.
Consulte commit 2bc31d1 , commit cc118a6 (28 de julho de 2015) por Jeff King ( peff) .
(Mesclado por Junio ​​C Hamano - gitster- no commit 824a0be , 19 de agosto de 2015)

refs: suporte negativo transfer.hideRefs

Se você ocultar uma hierarquia de referências usando a transfer.hideRefsconfiguração, não há como substituí-la posteriormente para "ocultá-la".
Este patch implementa uma ocultação "negativa" que faz com que as correspondências sejam imediatamente marcadas como não ocultas, mesmo que outra correspondência oculte.
Nós tomamos o cuidado de aplicar as correspondências na ordem inversa de como elas nos são alimentadas pelo mecanismo de configuração, pois isso permite que o nosso "último vencedor" usual trabalhe com precedência de configuração (e as entradas .git/config, por exemplo, serão substituídas /etc/gitconfig).

Então agora você pode fazer:

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

ocultar refs/secretem todos os repositórios, exceto um bit público em um repositório específico.


O Git 2.7 (Nov / Dez 2015) será aprimorado novamente:

Consulte commit 948bfa2 , commit 00b293e (05 de novembro de 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 de novembro de 2015) por Lukas Fleischer ( lfos) .
Ajudado por: Eric Sunshine ( sunshineco) .
(Mesclado por Jeff King - peff- no commit dbba85e , 20 de novembro de 2015)

config.txt: documentar a semântica hideRefscom namespaces

No momento, não há uma definição clara de como transfer.hideRefsdeve se comportar quando um espaço para nome é definido.
Explique que os hideRefsprefixos correspondem aos nomes separados nesse caso. É assim que os hideRefspadrões são atualmente tratados no pacote de recebimento.

hideRefs: adicione suporte para referências completas correspondentes

Além de combinar refs removidos, agora é possível adicionar hideRefspadrões com os quais o ref completo (sem strip) é comparado.
Para distinguir entre correspondências despojadas e completas, esses novos padrões devem ser prefixados com um circunflexo ( ^).

Daí a nova documentação :

transfer.hideRefs:

Se um espaço para nome estiver em uso, o prefixo do espaço para nome será retirado de cada referência antes de corresponder aos transfer.hiderefspadrões.
Por exemplo, se refs/heads/masterfor especificado no transfer.hideRefseo namespace atual é foo, em seguida, refs/namespaces/foo/refs/heads/master é omitida a partir dos anúncios, mas refs/heads/mastere refs/namespaces/bar/refs/heads/masterainda são anunciados como chamadas linhas "têm".
Para corresponder aos árbitros antes da remoção, adicione um ^na frente do nome do árbitro. Se você combinar !e ^, !deve ser especificado primeiro.


R .. menciona nos comentários a configuração uploadpack.allowAnySHA1InWant, que permite upload-packaceitar uma fetchsolicitação que solicita qualquer objeto. (O padrão é false).

Veja commit f8edeaa (novembro de 2016, Git v2.11.1) por David "novalis" Turner ( novalis) :

upload-pack: opcionalmente, permite buscar qualquer sha1

Parece um pouco tolo fazer uma verificação de acessibilidade no caso em que confiamos que o usuário acesse absolutamente tudo no repositório.

Além disso, é atrevido em um sistema distribuído - talvez um servidor anuncie uma ref, mas outro desde então teve um empurrão forçado nessa ref, e talvez as duas solicitações HTTP acabem direcionadas para esses servidores diferentes.


4
Você pode dar um exemplo mais completo de como criar um repo clone com apenas esse único commit? Eu tentei, mas falhei .. Obrigado!
Lars Bilke

1
Eu quero passar para o GitHub. Talvez eles não permitam isso.
Lars Bilke

2
@LarsBilke estamos falando de clone ou pull aqui, não push. E tenho certeza que o GitHub ainda não possui o Git 2.5 no servidor.
VonC 6/08/15

2
Agora, melhor ainda, não há uploadpack.allowAnySHA1InWanta penalidade de alcançabilidade-computação (e vetor DoS).
R .. GitHub PARE DE AJUDAR O GELO 26/06

1
Obrigado! Acho engraçado que eles o descrevam como "confie no acesso do usuário" em vez de "confie nos autores do repositório para não empurrar porcaria aleatória que eles não pretendem tornar públicos".
R .. GitHub Pare de ajudar o gelo

97

Você clona apenas uma vez; portanto, se você já possui um clone do repositório remoto, extrair dele não fará o download de tudo novamente. Apenas indique qual ramo você deseja obter ou busque as alterações e faça o checkout do commit que deseja.

Buscar em um novo repositório é muito barato em largura de banda, pois só fará o download das alterações que você não possui. Pense em termos de Git fazendo a coisa certa, com carga mínima.

O Git armazena tudo na .gitpasta. Um commit não pode ser buscado e armazenado isoladamente, ele precisa de todos os seus ancestrais. Eles estão inter-relacionados .


Para reduzir o tamanho do download, você pode solicitar ao git que busque apenas objetos relacionados a uma ramificação ou confirmação específica:

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

Isso fará o download apenas de confirmações contidas na ramificação remota branch (e apenas as que você sente falta) e as armazenará origin/branch. Você pode mesclar ou finalizar a compra.

Você também pode especificar apenas uma confirmação SHA1:

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

Isso fará o download apenas do commit do SHA-1 96de5297df870 especificado (e de seus ancestrais que você sente falta) e o armazenará como filial remota (não existente) origin/foo-commit.


3
Parece que você está confundindo o que significa clone. Quando você obtém alterações de um repositório remoto, não o clona, ​​apenas obtém os commits em seu histórico. Então você escolher qual comprometer você quiser verificar para fora, ou fundi-lo em sua história
charlesb

1
Ele ainda baixa muitos dados (430mb) com a busca do git. O commit necessário é de apenas alguns kbs. Não existe um comando especial para fazer isso realmente? E se eu quiser remover o repositório 'git buscado'? onde é guardado?
Varun Chitre

9
Isso está desatualizado agora. Temos a capacidade de executar um clone superficial e buscar um único commit . Agora, os clones rasos têm permissão para enviar e buscar normalmente, sem precisar conhecer o histórico completo do projeto; portanto, não é mais correto dizer que um commit não pode existir sozinho sem seus ancestrais. O que você diz sobre buscar após o clone inicial é muito verdadeiro, mas também temos opções ainda mais baratas.
Theodore Murdock

6
O último comando (usando a confirmação SHA1) não funciona para mim. O comando silenciosamente faz "alguma coisa" por um tempo e sai sem nenhuma mensagem ou efeito colateral aparente.
HRJ 26/08/16

1
@HRJ Sim, eu também encontrei isso no Ubuntu 16.04 com Git 2.7.4-0ubuntu1.3. No entanto, ao usar a 2.16.2-0ppa1~ubuntu16.04.1partir do PPA do git-core, isso funciona como deveria. Parece um bug que foi corrigido. Não foi possível encontrar uma referência a isso com uma pesquisa rápida. Se alguém puder me dar um ponteiro sobre isso, eu adoraria receber essa correção de volta.
gertvdijk

62

Eu fiz um puxão no meu repositório git:

git pull --rebase <repo> <branch>

Permitindo que o git puxasse todo o código da ramificação e depois fui redefinir o commit que me interessava.

git reset --hard <commit-hash>

Espero que isto ajude.


1
Nenhuma das respostas funcionou, essa aqui salvou minha vida! Muitíssimo obrigado!
michaeltintiuc

A redefinição - funcionou para mim após a clonagem! Obrigado.
Nick-ACNB

3
-1: comandos "destrutivos" como git reset --hard, quando compartilhados em soluções generalizadas, podem levar as pessoas a traps onde perdem dados (ou, nesse caso: em um estado em que recuperar seus dados não é trivial).
yaauie 7/02/19

54

Você pode simplesmente buscar um único commit de um repositório remoto com

git fetch <repo> <commit>

Onde,

  • <repo>pode ser um nome de repositório remoto (por exemplo origin) ou até mesmo um URL de repositório remoto (por exemplo https://git.foo.com/myrepo.git)
  • <commit> pode ser o commit do SHA1

por exemplo

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

depois de buscar o commit (e os ancestrais ausentes), você pode simplesmente fazer checkout com

git checkout FETCH_HEAD

Observe que isso o levará ao estado "cabeça desanexada".


10
Quando tento fetchuma rev específica como você faz lá, o git falha com o código de erro 1 e sem saída. Isso era algo que costumava funcionar em versões anteriores? (Eu sou v2.0.2.)
Jack O'Connor

2
Edit: Funciona se eu já tiver o commit local, como se eu já fiz um full fetch, embora nesse caso não tenha certeza de qual seja o uso.
Jack O'Connor

2
De fato, isso não parece funcionar mais para mim com o git 2.0.2. :(
Fluxo

2
git checkout FETCH_HEADajuda.
Lzl124631x

1
Este método não funciona com busca superficial (por exemplo --depth=1)!
kingmakerking

16

Você pode simplesmente buscar o repositório remoto com:

git fetch <repo>

Onde,

  • <repo>pode ser um nome de repositório remoto (por exemplo origin) ou até mesmo um URL de repositório remoto (por exemplo https://git.foo.com/myrepo.git)

por exemplo:

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

depois de buscar os repositórios, você pode mesclar os commits que você deseja (já que a pergunta é sobre recuperar um commit, em vez disso, você pode usar cherry-pick para escolher apenas um commit):

git merge <commit>
  • <commit> pode ser o commit do SHA1

por exemplo:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

ou

git merge 0a071603d87e0b89738599c160583a19a6d95545

se é a confirmação mais recente que você deseja mesclar, você também pode usar a variável FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD

Isso requer uma configuração de conta Git em uma máquina. Não funciona em uma conta de teste. Você tem algo que funciona em uma conta de teste?
JWW

o que você quer dizer ? você não pode fazer o git buscar?
Sérgio

Ummm, então o comando seria git config set uploadpack.allowReachableSHA1InWant ?
Alexander Mills

2

Isso funciona melhor:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

nomeie "temp" o que quiser ... esse ramo pode ficar órfão


Claramente não com versões git mais antigos como 1.8.x
Sorin

1

Finalmente, encontrei uma maneira de clonar commit específico usando git cherry-pick . Supondo que você não tenha nenhum repositório no local e esteja obtendo commit específico do remoto,

1) crie um repositório vazio no local e git init

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

3) origem do git fetch [isso não moverá seus arquivos para o espaço de trabalho local, a menos que você mescle]

4) git cherry-pick " Digite-comprimir-hash-que-você precisa "

Feito. Dessa forma, você terá apenas os arquivos desse commit específico no seu local.

Enter-long-commit-hash:

Você pode obter isso usando -> git log --pretty = oneline



0

Se o commit solicitado estiver nas solicitações pull do repositório remoto, você poderá obtê-lo pelo seu 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.