Como clonar um subdiretório apenas de um repositório Git?

1410

Eu tenho meu repositório Git que, na raiz, possui dois subdiretórios:

/finisht
/static

Quando isso estava no SVN , o /finishtcheck-out era feito em um local e o /staticcheck-out em outro lugar, da seguinte forma:

svn co svn+ssh://[email protected]/home/admin/repos/finisht/static static

Existe uma maneira de fazer isso com o Git?

Nick Sergeant
fonte
14
possível duplicata dos subdiretórios
Joachim Breitner
1
Para um usuário de 2014, qual o git clonecomando mais simples? Eu usei essa resposta simples . Se há algo mais simples, por favor comentário
Peter Krauss
Para aqueles que tentam clonar o conteúdo do repositório (não criando a pasta raiz), esta é uma solução muito fácil: stackoverflow.com/questions/6224626/…
Marc
@JoachimBreitner: Essa pergunta é sobre verificar subdiretórios no Git (o que é fácil), enquanto essa pergunta é sobre clonar subdiretórios no Git (o que é impossível).
Jörg W Mittag
@NickSergeant: A partir do Git 2.19, lançado há 3 semanas, isso é finalmente possível, como pode ser visto nesta resposta: stackoverflow.com/a/52269934/2988 Considere aceitar esse agora. Nota: no Git 2.19, apenas o suporte do lado do cliente é implementado, o suporte do lado do servidor ainda está ausente, portanto, funciona apenas ao clonar repositórios locais. Observe também que grandes hosts do Git, por exemplo, o GitHub, na verdade, não usam o servidor Git, eles usam sua própria implementação; portanto, mesmo que o suporte apareça no servidor Git, isso não significa automaticamente que ele funciona nos hosts do Git. (OTOH, poderiam aplicar-lo mais rápido.)
Jörg W Mittag

Respostas:

612

EDIT : A partir do Git 2.19, isso é finalmente possível, como pode ser visto nesta resposta .

Considere votar essa resposta.

Nota: no Git 2.19, apenas o suporte do lado do cliente é implementado, o suporte do lado do servidor ainda está ausente e, portanto, funciona apenas ao clonar repositórios locais. Observe também que grandes hosts do Git, por exemplo, o GitHub, na verdade não usam o servidor Git, eles usam sua própria implementação; portanto, mesmo que o suporte apareça no servidor Git, isso não significa automaticamente que ele funcione nos hosts do Git. (OTOH, como eles não usam o servidor Git, eles poderiam implementá-lo mais rapidamente em suas próprias implementações antes de aparecer no servidor Git.)


Não, isso não é possível no Git.

A implementação de algo assim no Git seria um esforço substancial e significaria que a integridade do repositório do lado do cliente não poderia mais ser garantida. Se você estiver interessado, procure discussões sobre "sparse clone" e "sparse fetch" na lista de discussão do git.

Em geral, o consenso na comunidade Git é que, se você tiver vários diretórios sempre com check-out independente, esses são realmente dois projetos diferentes e devem residir em dois repositórios diferentes. Você pode colá-los novamente usando os Submodules Git .

Jörg W Mittag
fonte
6
Dependendo do cenário, convém usar a subárvore git em vez do submodule git. Veja alumnit.ca/~apenwarr/log/?m=200904#30
C Pirate
9
@StijndeWitt: verificações esparsas acontecem durante git-read-tree, o que é muito tempo depois get-fetch. A questão não era verificar apenas um subdiretório, mas clonar apenas um subdiretório. Não vejo como caixas esparsas poderiam fazer isso, uma vez que git-read-treeé executado após o clone já ter sido concluído.
Jörg W Mittag
9
Em vez de "stub", você gostaria de excluir esta resposta para que a Chronial possa flutuar até o topo? Você não pode excluí-lo, porque é aceito, mas um moderador pode. Você manteria a reputação que ganhou com isso, já que é muito antiga. (Eu me deparei com isso porque alguém o sinalizou como "somente link". :-)
Cody Gray
1
@CodyGray: A resposta cronológica ainda clona todo o repositório, e não apenas um subdiretório. (O último parágrafo até diz explicitamente.) Não é possível clonar apenas um subdiretório no Git. O protocolo de rede não suporta, o formato de armazenamento não suporta. Cada resposta única para essa pergunta sempre clona o repositório inteiro. A pergunta é simples e sim / não, e a resposta é de dois caracteres: Não. Se houver, minha resposta é desnecessariamente longa , não curta.
Jörg W Mittag
1
@ JörgWMittag: A resposta de Ciro Santili parece contradizê-lo.
Dan Dascalescu 4/11
1525

O que você está tentando fazer é chamado de verificação esparsa , e esse recurso foi adicionado no git 1.7.0 (fevereiro de 2012). As etapas para executar um clone esparso são as seguintes:

mkdir <repo>
cd <repo>
git init
git remote add -f origin <url>

Isso cria um repositório vazio com seu controle remoto e busca todos os objetos, mas não os verifica. Então faça:

git config core.sparseCheckout true

Agora você precisa definir quais arquivos / pastas você deseja realmente fazer check-out. Isso é feito listando-os em .git/info/sparse-checkout, por exemplo:

echo "some/dir/" >> .git/info/sparse-checkout
echo "another/sub/tree" >> .git/info/sparse-checkout

Por último, mas não menos importante, atualize seu repositório vazio com o estado do controle remoto:

git pull origin master

Agora você terá os arquivos "retirados" para some/dire another/sub/treeno seu sistema de arquivos (com esses caminhos ainda) e nenhum outro caminho presente.

Você pode querer dar uma olhada no tutorial estendido e provavelmente deve ler a documentação oficial para uma verificação esparsa .

Como uma função:

function git_sparse_clone() (
  rurl="$1" localdir="$2" && shift 2

  mkdir -p "$localdir"
  cd "$localdir"

  git init
  git remote add -f origin "$rurl"

  git config core.sparseCheckout true

  # Loops over remaining args
  for i; do
    echo "$i" >> .git/info/sparse-checkout
  done

  git pull origin master
)

Uso:

git_sparse_clone "http://github.com/tj/n" "./local/location" "/bin"

Observe que isso ainda fará o download de todo o repositório do servidor - apenas o tamanho da finalização da compra. No momento, não é possível clonar apenas um único diretório. Mas se você não precisar do histórico do repositório, poderá pelo menos economizar largura de banda criando um clone superficial. Veja a resposta de udondan abaixo para obter informações sobre como combinar clone superficial e caixa esparsa.


A partir do git 2.25.0 (janeiro de 2020), um comando experimental de verificação esparsa é adicionado ao git:

git sparse-checkout init
# same as: 
git config core.sparseCheckout true

git sparse-checkout set "A/B"
# same as:
echo "A/B" >> .git/info/sparse-checkout

git sparse-checkout list
# same as:
cat .git/info/sparse-checkout
Chronial
fonte
14
na Apple, o perímetro '-f' não funciona. apenas fazer origem add remoto git <url> sem -f
Anno2001
135
É uma melhoria, mas ainda precisa fazer o download e armazenar uma cópia completa do repositório remoto na origem, o que pode ser evitado se ele estiver interessado apenas em partes da base de código (ou se houver subpastas de documentação como no meu caso) )
a1an
56
Existe uma maneira de clonar o conteúdo do diretório desejado (não o próprio diretório) diretamente no meu repositório? Por exemplo, eu quero clonar o conteúdo de https://github.com/Umkus/nginx-boilerplate/tree/master/srcright into/etc/nginx
mac
25
@Chronial, @ErikE: vocês dois estão certos / errados: P O git remote addcomando não implica uma busca, mas git remote add -f, como usado aqui, sim! É isso que -fsignifica.
Ntc2 16/05
21
Usando isso, --depth=1eu clonei o Chromium Devtools em 338 MB em vez de 4,9 GB de histórico completo da fonte + do Blink. Excelente.
Rudie
444

git clone --filter do Git 2.19

Essa opção realmente ignora a busca de objetos desnecessários do servidor. Também incluindo o --filter=tree:0Git 2.20 e o --filter=combinefiltro composto adicionado no Git 2.24, terminamos com:

git clone \
  --depth 1 \
  --filter=combine:blob:none+tree:0 \
  --no-checkout \
  "file://$(pwd)/server_repo" \
  local_repo \
;
cd local_repo
git checkout master -- mydir/

O servidor deve ser configurado com:

git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

Foi feita uma extensão ao protocolo remoto Git para oferecer suporte a esse recurso v2.19.0e, na verdade, pular a busca de objetos desnecessários, mas não há suporte para o servidor no momento. Mas já pode ser testado localmente.

Divisão de comando:

O formato de --filterestá documentado emman git-rev-list .

Documentos na árvore Git:

Teste

#!/usr/bin/env bash
set -eu

list-objects() (
  git rev-list --all --objects
  echo "master commit SHA: $(git log -1 --format="%H")"
  echo "mybranch commit SHA: $(git log -1 --format="%H")"
  git ls-tree master
  git ls-tree mybranch | grep mybranch
  git ls-tree master~ | grep root
)

# Reproducibility.
export GIT_COMMITTER_NAME='a'
export GIT_COMMITTER_EMAIL='a'
export GIT_AUTHOR_NAME='a'
export GIT_AUTHOR_EMAIL='a'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00+0000'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'

rm -rf server_repo local_repo
mkdir server_repo
cd server_repo

# Create repo.
git init --quiet
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

# First commit.
# Directories present in all branches.
mkdir d1 d2
printf 'd1/a' > ./d1/a
printf 'd1/b' > ./d1/b
printf 'd2/a' > ./d2/a
printf 'd2/b' > ./d2/b
# Present only in root.
mkdir 'root'
printf 'root' > ./root/root
git add .
git commit -m 'root' --quiet

# Second commit only on master.
git rm --quiet -r ./root
mkdir 'master'
printf 'master' > ./master/master
git add .
git commit -m 'master commit' --quiet

# Second commit only on mybranch.
git checkout -b mybranch --quiet master~
git rm --quiet -r ./root
mkdir 'mybranch'
printf 'mybranch' > ./mybranch/mybranch
git add .
git commit -m 'mybranch commit' --quiet

echo "# List and identify all objects"
list-objects
echo

# Restore master.
git checkout --quiet master
cd ..

# Clone. Don't checkout for now, only .git/ dir.
git clone --depth 1 --quiet --no-checkout --filter=blob:none "file://$(pwd)/server_repo" local_repo
cd local_repo

# List missing objects from master.
echo "# Missing objects after --no-checkout"
git rev-list --all --quiet --objects --missing=print
echo

echo "# Git checkout fails without internet"
mv ../server_repo ../server_repo.off
! git checkout master
echo

echo "# Git checkout fetches the missing directory from internet"
mv ../server_repo.off ../server_repo
git checkout master -- d1/
echo

echo "# Missing objects after checking out d1"
git rev-list --all --quiet --objects --missing=print

GitHub upstream .

Saída no Git v2.19.0:

# List and identify all objects
c6fcdfaf2b1462f809aecdad83a186eeec00f9c1
fc5e97944480982cfc180a6d6634699921ee63ec
7251a83be9a03161acde7b71a8fda9be19f47128
62d67bce3c672fe2b9065f372726a11e57bade7e
b64bf435a3e54c5208a1b70b7bcb0fc627463a75 d1
308150e8fddde043f3dbbb8573abb6af1df96e63 d1/a
f70a17f51b7b30fec48a32e4f19ac15e261fd1a4 d1/b
84de03c312dc741d0f2a66df7b2f168d823e122a d2
0975df9b39e23c15f63db194df7f45c76528bccb d2/a
41484c13520fcbb6e7243a26fdb1fc9405c08520 d2/b
7d5230379e4652f1b1da7ed1e78e0b8253e03ba3 master
8b25206ff90e9432f6f1a8600f87a7bd695a24af master/master
ef29f15c9a7c5417944cc09711b6a9ee51b01d89
19f7a4ca4a038aff89d803f017f76d2b66063043 mybranch
1b671b190e293aa091239b8b5e8c149411d00523 mybranch/mybranch
c3760bb1a0ece87cdbaf9a563c77a45e30a4e30e
a0234da53ec608b54813b4271fbf00ba5318b99f root
93ca1422a8da0a9effc465eccbcb17e23015542d root/root
master commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
mybranch commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
040000 tree b64bf435a3e54c5208a1b70b7bcb0fc627463a75    d1
040000 tree 84de03c312dc741d0f2a66df7b2f168d823e122a    d2
040000 tree 7d5230379e4652f1b1da7ed1e78e0b8253e03ba3    master
040000 tree 19f7a4ca4a038aff89d803f017f76d2b66063043    mybranch
040000 tree a0234da53ec608b54813b4271fbf00ba5318b99f    root

# Missing objects after --no-checkout
?f70a17f51b7b30fec48a32e4f19ac15e261fd1a4
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb
?308150e8fddde043f3dbbb8573abb6af1df96e63

# Git checkout fails without internet
fatal: '/home/ciro/bak/git/test-git-web-interface/other-test-repos/partial-clone.tmp/server_repo' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

# Git checkout fetches the missing directory from internet
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.

# Missing objects after checking out d1
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb

Conclusões: todos os blobs de fora d1/estão ausentes. Por exemplo 0975df9b39e23c15f63db194df7f45c76528bccb, o que d2/bnão existe após o check-outd1/a .

Note-se que root/roote mybranch/mybranchtambém estão desaparecidos, mas --depth 1esconde que a partir da lista de arquivos ausentes. Se você remover --depth 1, eles serão exibidos na lista de arquivos ausentes.

Eu tenho um sonho

Esse recurso pode revolucionar o Git.

Imagine ter toda a base de código da sua empresa em um único repositório, sem ferramentas feias de terceirosrepo .

Imagine armazenar enormes blobs diretamente no repositório, sem extensões feias de terceiros .

Imagine se o GitHub permitiria metadados por arquivo / diretório, como estrelas e permissões, para que você possa armazenar todas as suas coisas pessoais em um único repositório.

Imagine se os submódulos foram tratados exatamente como diretórios regulares : basta solicitar um SHA em árvore e um mecanismo semelhante ao DNS resolve sua solicitação , primeiro procurando no local~/.git , depois nos servidores mais próximos (espelho / cache da empresa) e terminando no GitHub.

Ciro Santilli adicionou uma nova foto
fonte
Estranhamente, no MacOS com git versão 2.20.1 (Apple Git-117), ele se queixa de que "vários filtro-specs não podem ser combinados"
Muru
1
Infelizmente, não há sorte com a versão macOS git. fatal: invalid filter-spec 'combine:blob:none+tree:0'Obrigado mesmo assim! Talvez ele funcione com versões mais recentes.
Muru
1
Isso falha ao testá-lo no Windows 10 usando o GIT 2.24.1 (gera toneladas de "não é possível ler o arquivo sha1 de .." + "O link do arquivo xxx falhou."). Funcionou como um encanto com a mesma versão no Linux.
Oyvind
1
@Ciro Santilli Isso ainda falha com "não é possível ler o arquivo sha1 de ..." na versão 2.26.1.windows.1 do git. Abri um relatório de erro: github.com/git-for-windows/git/issues/2590
nharrer 18/04
1
@ nharrer obrigado pela informação!
Ciro Santilli escreveu: 18/04
405

Você pode combinar os recursos de caixa esparsa e clone superficial . O clone superficial corta o histórico e a verificação esparsa apenas puxa os arquivos correspondentes aos seus padrões.

git init <repo>
cd <repo>
git remote add origin <url>
git config core.sparsecheckout true
echo "finisht/*" >> .git/info/sparse-checkout
git pull --depth=1 origin master

Você precisará do mínimo git 1.9 para que isso funcione. Eu mesmo testei com 2.2.0 e 2.2.2.

Dessa forma, você ainda poderá pressionar , o que não é possível com git archive.

udondan
fonte
21
Isso é útil e pode ser a melhor resposta disponível, mas ainda clona o conteúdo do qual você não se importa (se estiver no ramo que você puxa), mesmo que não apareça no checkout.
No25
1
Qual é a sua versão do git? De acordo com a ajuda do git, a opção de profundidade está disponível?
precisa saber é o seguinte
2
não funciona para mim quando o último comando não é, git pull --depth=1 origin mastermas git pull --depth=1 origin <any-other-branch>. isso é tão estranho, veja minha pergunta aqui: stackoverflow.com/questions/35820630/…
Shuman 6/6
5
No Windows, a penúltima linha precisa omitir as aspas, ou a solicitação falha.
Nateirvin 31/03
4
Isso ainda baixa todos os dados! Encontrado esta solução, usando svn: stackoverflow.com/a/18324458/2302437
electronix384128
157

Para outros usuários que desejam apenas baixar um arquivo / pasta do github, basta usar:

svn export <repo>/trunk/<folder>

por exemplo

svn export https://github.com/lodash/lodash.com/trunk/docs

(sim, aqui está o svn. aparentemente em 2016 você ainda precisa do svn para fazer o download de alguns arquivos do github)

Cortesia: Faça o download de uma única pasta ou diretório de um repositório do GitHub

Importante - Atualize o URL do github e substitua/tree/master/ por '/ trunk /'.

Como script bash:

git-download(){
    folder=${@/tree\/master/trunk}
    folder=${folder/blob\/master/trunk}
    svn export $folder
}

Nota Este método baixa uma pasta, não a clona / faz checkout. Você não pode enviar as alterações de volta ao repositório. Por outro lado, isso resulta em um download menor comparado ao checkout esparso ou raso.

Anona112
fonte
9
única versão que funcionou para mim com o github. Os comandos git fizeram check-out de arquivos> 10k, o svn exporta apenas os 700 que eu queria. Obrigado!
Christopher Lörken
4
Tentei fazer isso com, https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/trunk/udacitymas obtive o svn: E170000: URL 'https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/trunk/udacity' doesn't existerro :(
zthomas.nc 19/02
9
@ zthomas.nc Você precisa remover o 'trunk' anterior à udacidade, e substituir / tree / master / por / trunk /.
Speedy
2
Esse comando foi o que funcionou para mim! Eu só queria obter uma cópia de um arquivo de um repositório para poder modificá-lo localmente. Bom e velho SVN para o resgate!
Michael J
3
funciona, mas parece lento. Demora um pouco para começar e, em seguida, os arquivos rolar por forma relativamente lenta
Aryeh Beitz
73

Se você nunca planeja interagir com o repositório a partir do qual você clonou, poderá fazer um clone git completo e reescrever seu repositório usando git filter-branch --subdirectory-filter . Dessa forma, pelo menos a história será preservada.

hillu
fonte
11
Para pessoas que não conhecem o comando, égit filter-branch --subdirectory-filter <subdirectory>
Jaime Hablutzel
9
Esse método tem a vantagem de que o subdiretório que você escolher se torna a raiz do novo repositório, que é exatamente o que eu quero.
Andrew Schulman
Essa é definitivamente a melhor e mais fácil abordagem de usar. Aqui está um comando de uma etapa usando subdiretório-filtrogit clone https://github.com/your/repo_xx.git && cd repo_xx && git filter-branch --subdirectory-filter repo_xx_subdir
Alex
66

Isso parece muito mais simples:

git archive --remote=<repo_url> <branch> <path> | tar xvf -
ErichBSchulz
fonte
17
Quando faço isso no github, fico fatal: Operação não suportada pelo protocolo. Fim inesperado do fluxo de comandos
Michael Fox
1
O erro do protocolo pode ser devido a HTTPS ou: no URL do repositório. Também pode ser devido à falta da chave ssh.
Umair A.
2
Se você estiver usando github você pode usar svn exportem vez
Milo Wielondek
2
Não funcionará com o Github -> Comando inválido: 'git-upload-archive' xxx / yyy.git '' Você parece estar usando o ssh para clonar um URL do git: //. Verifique se a opção de configuração core.gitProxy e a variável de ambiente GIT_PROXY_COMMAND NÃO estão definidas. fatal: O lado remoto desligou inesperadamente
Nianliang
3
A razão pela qual isso não funciona com o GitHub: "Não oferecemos suporte ao uso do git-archive para extrair um arquivo diretamente do GitHub. Você pode clonar o repositório localmente e executar o git-archive ou clicar no botão Download ZIP em a página do repositório ". github.com/xuwupeng2000/capistrano-scm-gitcopy/issues/16
Donn Lee
63

O Git 1.7.0 possui "verificações esparsas". Consulte “core.sparseCheckout” na página de manual do git config , “Sparse checkout” na página de manual da árvore de leitura do git e “Skip-worktree bit” na página de manual do git update-index .

A interface não é tão conveniente quanto os SVNs (por exemplo, não há como fazer uma verificação esparsa no momento de um clone inicial), mas a funcionalidade básica na qual interfaces mais simples podem ser construídas está agora disponível.

Chris Johnsen
fonte
37

Não é possível clonar subdiretórios apenas com o Git, mas abaixo estão algumas soluções alternativas.

Filial do filtro

Você pode reescrever o repositório para ter a aparência de trunk/public_html/sua raiz do projeto e descartar todo o outro histórico (usando filter-branch), tente o ramo de check-out:

git filter-branch --subdirectory-filter trunk/public_html -- --all

Notas: O --que separa as opções de ramificação de filtro das opções de revisão e --allpara reescrever todas as ramificações e tags. Todas as informações, incluindo tempos de confirmação originais ou informações de mesclagem, serão preservadas . Esse comando honra o .git/info/graftsarquivo e as referências no refs/replace/espaço para nome; portanto, se você tiver enxertos ou substituições refsdefinidos, a execução desse comando os tornará permanentes.

Atenção! O histórico reescrito terá nomes de objetos diferentes para todos os objetos e não convergirá com a ramificação original. Você não poderá enviar e distribuir facilmente a ramificação reescrita na parte superior da ramificação original. Por favor, não use este comando se você não souber todas as implicações e evite usá-lo de qualquer maneira, se uma simples confirmação simples for suficiente para corrigir seu problema.


Pouca compra

Aqui estão algumas etapas simples com abordagem de checkout esparso que preencherão o diretório de trabalho escassamente, para que você possa dizer ao Git quais pastas ou arquivos no diretório de trabalho valem a pena conferir.

  1. Clone o repositório como de costume ( --no-checkouté opcional):

    git clone --no-checkout git@foo/bar.git
    cd bar
    

    Você pode pular esta etapa, se o seu repositório já tiver clonado.

    Dica: para repositórios grandes, considere o clone superficial ( --depth 1) fazer checkout apenas da revisão mais recente ou --single-branchapenas.

  2. Ativar sparseCheckoutopção:

    git config core.sparseCheckout true
    
  3. Especifique a (s) pasta (s) para checkout esparso ( sem espaço no final):

    echo "trunk/public_html/*"> .git/info/sparse-checkout
    

    ou editar .git/info/sparse-checkout.

  4. Faça o checkout da filial (por exemplo master):

    git checkout master
    

Agora você deve ter selecionado pastas no diretório atual.

Você pode considerar links simbólicos se tiver muitos níveis de diretórios ou ramo de filtragem.


kenorb
fonte
A ramificação Filter ainda permitiria pull?
sam
2
@ sam: não. filter-branchreescreveria as confirmações pai, para que tivessem IDs SHA1 diferentes e, portanto, sua árvore filtrada não teria confirmações em comum com a árvore remota. git pullnão saberia de onde tentar se juntar.
Peter Cordes
Essa abordagem é principalmente uma resposta satisfatória ao meu caso.
Abbas
10

Acabei de escrever um script para o GitHub .

Uso:

python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>
david_adler
fonte
11
Para sua informação, isso é apenas para o GitHub .
Sz.
9
E aparentemente isso é para baixar um diretório, não clonar uma parte de um repositório com todos os seus metadados ... certo?
LarsH 15/10
5
Você deve incluir seu código aqui e não em outro lugar.
jww 31/12/18
urllib2.HTTPError: Erro HTTP 403: limite de taxa excedido
diyism
9

Isso clonará uma pasta específica e removerá todo o histórico não relacionado a ela.

git clone --single-branch -b {branch} [email protected]:{user}/{repo}.git
git filter-branch --subdirectory-filter {path/to/folder} HEAD
git remote remove origin
git remote add origin [email protected]:{user}/{new-repo}.git
git push -u origin master
BARJ
fonte
Aqui estejam dragões. Você é recebido por AVISO: o git-filter-branch tem uma abundância de truques que geram reescritas de histórico mutiladas . Os documentos do git-filter-branch têm uma lista de avisos bastante longa.
Oyvind
6

Aqui está um script de shell que escrevi para o caso de uso de um único check-out esparso de subdiretório

coSubDir.sh

localRepo=$1
remoteRepo=$2
subDir=$3


# Create local repository for subdirectory checkout, make it hidden to avoid having to drill down to the subfolder
mkdir ./.$localRepo
cd ./.$localRepo
git init
git remote add -f origin $remoteRepo
git config core.sparseCheckout true

# Add the subdirectory of interest to the sparse checkout.
echo $subDir >> .git/info/sparse-checkout

git pull origin master

# Create convenience symlink to the subdirectory of interest
cd ..
ln -s ./.$localRepo/$subDir $localRepo
jxramos
fonte
2
Bom roteiro, apenas a algo que deve ser corrigido é a ligação simbólica, deve ser ln -s ./.$localRepo/$subDir $localRepo, em vez deln -s ./.$localRepo$subDir $localRepo
valentin_nasta
2

Eu escrevi um .gitconfig [alias]para realizar um "checkout esparso". Confira (sem trocadilhos):

No Windows, execute cmd.exe

git config --global alias.sparse-checkout "!f(){ [ $# -eq 2 ] && L=${1##*/} L=${L%.git} || L=$2; mkdir -p \"$L/.git/info\" && cd \"$L\" && git init --template= && git remote add origin \"$1\" && git config core.sparseCheckout 1; [ $# -eq 2 ] && echo \"$2\" >> .git/info/sparse-checkout || { shift 2; for i; do echo $i >> .git/info/sparse-checkout; done }; git pull --depth 1 origin master;};f"

De outra forma:

git config --global alias.sparse-checkout '!f(){ [ $# -eq 2 ] && L=${1##*/} L=${L%.git} || L=$2; mkdir -p "$L/.git/info" && cd "$L" && git init --template= && git remote add origin "$1" && git config core.sparseCheckout 1; [ $# -eq 2 ] && echo "$2" >> .git/info/sparse-checkout || { shift 2; for i; do echo $i >> .git/info/sparse-checkout; done }; git pull --depth 1 origin master;};f'

Uso :

# Makes a directory ForStackExchange with Plug checked out
git sparse-checkout https://github.com/YenForYang/ForStackExchange Plug

# To do more than 1 directory, you have to specify the local directory:
git sparse-checkout https://github.com/YenForYang/ForStackExchange ForStackExchange Plug Folder

Os git configcomandos são 'minificados' para conveniência e armazenamento, mas aqui está o alias expandido:

# Note the --template= is for disabling templates.
# Feel free to remove it if you don't have issues with them (like I did)
# `mkdir` makes the .git/info directory ahead of time, as I've found it missing sometimes for some reason
f(){
    [ "$#" -eq 2 ] && L="${1##*/}" L=${L%.git} || L=$2;
    mkdir -p "$L/.git/info"
        && cd "$L"
        && git init --template=
        && git remote add origin "$1"
        && git config core.sparseCheckout 1;
    [ "$#" -eq 2 ]
        && echo "$2" >> .git/info/sparse-checkout
        || {
            shift 2;
            for i; do
                echo $i >> .git/info/sparse-checkout;
            done
        };
    git pull --depth 1 origin master;
};
f
YenForYang
fonte
Por que isso funciona L=${1##*/} L=${L%.git}:? O espaço é um operador?
Gulzt
2

Usando Linux? E só quer fácil acesso e limpeza da árvore de trabalho? sem incomodar o restante do código em sua máquina. tente links simbólicos !

git clone https://github.com:{user}/{repo}.git ~/my-project
ln -s ~/my-project/my-subfolder ~/Desktop/my-subfolder

Teste

cd ~/Desktop/my-subfolder
git status
Nasir Iqbal
fonte
1

Apenas para esclarecer algumas das ótimas respostas aqui, as etapas descritas em muitas das respostas assumem que você já possui um repositório remoto em algum lugar.

Dado: um repositório git existente, por exemplo [email protected]:some-user/full-repo.git, com um ou mais diretórios que você deseja extrair independentemente do restante do repositório, por exemplo , diretórios nomeados app1eapp2

Supondo que você tenha um repositório git como o descrito acima ...

Então: você pode executar etapas como a seguir para extrair apenas diretórios específicos desse repositório maior:

mkdir app1
cd app1
git init
git remote add origin [email protected]:some-user/full-repo.git
git config core.sparsecheckout true
echo "app1/" >> .git/info/sparse-checkout
git pull origin master

Por engano, pensei que as opções de checkout esparso deviam ser definidas no repositório original: esse não é o caso. Você define quais diretórios deseja localmente, antes de extrair do controle remoto. Espero que este esclarecimento ajude outra pessoa.

Everett
fonte
0

Embora eu odeie ter que usar svn ao lidar com reposições git: / eu uso isso o tempo todo;

function git-scp() (
  URL="$1" && shift 1
  svn export ${URL/blob\/master/trunk}
)

Isso permite que você copie do URL do github sem modificação. Uso;

--- /tmp » git-scp https://github.com/dgraph-io/dgraph/blob/master/contrib/config/kubernetes/helm                                                                                                                  1 ↵
A    helm
A    helm/Chart.yaml
A    helm/README.md
A    helm/values.yaml
Exported revision 6367.

--- /tmp » ls | grep helm
Permissions Size User    Date Modified    Name
drwxr-xr-x     - anthony 2020-01-07 15:53 helm/
expelledboyboy
fonte
0

Se você está realmente interessado nos arquivos de revisão mais recentes de um diretório, o Github permite fazer o download de um repositório como um arquivo Zip, que não contém histórico. Portanto, o download é muito mais rápido.

weberjn
fonte
0

Então, eu tentei de tudo nesta etapa e nada funcionou para mim ... Acontece que na versão 2.24 do Git (a que acompanha o cpanel no momento desta resposta), você não precisa fazer isso

echo "wpm/*" >> .git/info/sparse-checkout

tudo o que você precisa é o nome da pasta

wpm/*

Então, em resumo, você faz isso

git config core.sparsecheckout true

você edita o arquivo .git / info / sparse-checkout e adiciona os nomes das pastas (um por linha) com / * no final para obter subpastas e arquivos

wpm/*

Salve e execute o comando checkout

git checkout master

O resultado foi a pasta esperada do meu repo e nada mais Voto positivo se isso funcionou para você

Patrick Simard
fonte