Domanda

Ho il mio repository Git che, alla radice, ha due sottodirectory:

/finisht
/static

Quando era in SVN , / finisht è stato estratto in un posto, mentre / static è stato estratto altrove, in questo modo:

svn co svn+ssh://admin@domain.com/home/admin/repos/finisht/static static

C'è un modo per farlo con Git?

È stato utile?

Soluzione

MODIFICA : A partire da Git 2.19, questo è finalmente possibile, come si può vedere in questa risposta: https://stackoverflow.com/a/52269934/2988 .

Valuta la possibilità di votare quella risposta.

Nota: in Git 2.19 è implementato solo il supporto lato client, manca ancora il supporto lato server, quindi funziona solo quando si clonano repository locali. Si noti inoltre che i grandi hoster Git, ad es. GitHub, in realtà non usa il server Git, usa la propria implementazione, quindi anche se il supporto viene mostrato nel server Git, ciò non significa automaticamente che funzioni sugli hoster Git. (OTOH, poiché non usano il server Git, potrebbero implementarlo più velocemente nelle proprie implementazioni prima che venga visualizzato nel server Git.)


No, non è possibile in Git.

L'implementazione di qualcosa del genere in Git sarebbe uno sforzo sostanziale e significherebbe che l'integrità del repository sul lato client non può più essere garantita. Se sei interessato, cerca discussioni su " clone sparso " e " sparse fetch " sulla mailing list di git.

In generale, il consenso nella comunità Git è che se hai diverse directory che vengono sempre estratte in modo indipendente, allora si tratta in realtà di due progetti diversi e dovrebbero vivere in due diversi repository. Puoi incollarli di nuovo insieme usando Git Submodules .

Altri suggerimenti

Quello che stai cercando di fare è chiamato checkout rado , e quella funzionalità è stata aggiunta in git 1.7.0 (febbraio 2012). I passaggi per eseguire un clone sparso sono i seguenti:

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

Questo crea un repository vuoto con il telecomando e recupera tutti gli oggetti ma non li controlla. Quindi fai:

git config core.sparseCheckout true

Ora devi definire quali file / cartelle vuoi effettivamente estrarre. Questo viene fatto elencandoli in .git / info / sparse-checkout , ad esempio:

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

Ultimo ma non meno importante, aggiorna il tuo repository vuoto con lo stato dal telecomando:

git pull origin master

Ora avrai i file " estratti " per some / dir e another / sub / tree sul tuo file system (con quei percorsi ancora) e nessun altro percorso presente.

Potresti dare un'occhiata al tutorial esteso e probabilmente dovresti leggere la documentazione ufficiale per checkout rado .

Come funzione:

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
)

Utilizzo:

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

Nota che questo scaricherà comunque l'intero repository dal server & # 8211; solo la cassa è di dimensioni ridotte. Al momento non è possibile clonare solo una singola directory. Ma se non hai bisogno della cronologia del repository, puoi almeno risparmiare sulla larghezza di banda creando un clone superficiale. Vedi la risposta di udondan di seguito per informazioni su come combinare shallow clone e checkout sparso.

Puoi combinare le funzioni checkout sparse e shallow clone . Il clone superficiale taglia la cronologia e il checkout sparso estrae solo i file corrispondenti ai tuoi schemi.

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

Per funzionare, avrai bisogno di git minimo 1.9. Ho provato io stesso solo con 2.2.0 e 2.2.2.

In questo modo sarai ancora in grado di spingere , cosa impossibile con git archive .

git clone --filter da Git 2.19

Questa opzione salterà effettivamente il recupero di oggetti non necessari dal server:

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

Il server deve essere configurato con:

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

È stata creata un'estensione al protocollo remoto Git per supportare questa funzione nella v2.19.0, ma al momento non è disponibile alcun supporto server. Ma può già essere testato localmente.

TODO: --filter = blob: none salta tutti i BLOB, ma continua a recuperare tutti gli oggetti dell'albero. Ma su un normale repository, questo dovrebbe essere minuscolo rispetto ai file stessi, quindi questo è già abbastanza buono. Chiesto a: https://www.spinics.net/lists/git/msg342006.html Gli sviluppatori hanno risposto che --filter = tree: 0 è in lavorazione per farlo. è stato aggiunto in 2.20 .

Ricorda che --depth 1 implica già --single-branch , vedi anche: Come posso clonare un singolo branch in Git?

file: // $ (percorso) è necessario per superare il protocollo git clone shenanigans: Come eseguire la clonazione superficiale di un repository git locale con un percorso relativo?

Il formato di --filter è documentato su man git-rev-list .

Documenti sull'albero Git:

Provalo

#!/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 .

Output in 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

Conclusioni: mancano tutti i BLOB esterni a d1 / . Per esempio. 0975df9b39e23c15f63db194df7f45c76528bccb , che è d2 / b non è presente dopo aver verificato d1 / a .

Nota che mancano anche root / root e mybranch / mybranch , ma --depth 1 lo nasconde dall'elenco dei file mancanti . Se rimuovi --depth 1 , verranno visualizzati nell'elenco dei file mancanti.

Ho un sogno

Questa funzione potrebbe rivoluzionare Git.

Immagina di avere tutta la base di codice della tua impresa in un singolo repository senza brutti strumenti di terze parti come repo .

Immagina archiviare enormi BLOB direttamente nel repository senza brutti terzi estensioni .

Immagina se GitHub consentirebbe metadati per file / directory come stelle e autorizzazioni, quindi puoi archiviare tutte le tue cose personali in un unico repository.

Immagina se i sottomoduli sono stati trattati esattamente come le normali directory : basta richiedere un albero SHA e un un meccanismo simile a DNS risolve la tua richiesta , per prima cosa sul tuo ~ / locale. , quindi prima verso i server più vicini (mirror / cache della tua azienda) e finendo su GitHub.

Per gli altri utenti che vogliono semplicemente scaricare un file / cartella da github, usa semplicemente:

svn export <repo>/trunk/<folder>

per es.

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

(sì, è svn qui. Apparentemente nel 2016 hai ancora bisogno di svn per scaricare semplicemente alcuni file github)

Per gentile concessione: Scarica una singola cartella o directory da un repository GitHub

Importante - Assicurati di aggiornare l'URL github e sostituire / tree / master / con '/ trunk /'.

Come script bash:

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

Nota Questo metodo scarica una cartella, non la clona / verifica. Non è possibile rinviare le modifiche al repository. D'altra parte, ciò si traduce in un download più piccolo rispetto al checkout scarso o al checkout superficiale.

Se non prevedi mai di interagire con il repository da cui hai clonato, puoi fare un clone git completo e riscrivere il tuo repository usando git filter-branch --subdirectory-filter . In questo modo, almeno la storia verrà preservata.

Git 1.7.0 ha & # 8220; checkout sparsi & # 8221 ;. Vedere & # 8220; core.sparseCheckout & # 8221; nella manpage git config , & # 8220; Pagamento sparso & # 8221; nel git read-tree manpage e & # 8220; Bit Skip-worktree & # 8221; nel git update-index manpage .

L'interfaccia non è così conveniente come quella di SVN (ad es. non c'è modo di effettuare un checkout sparso al momento di un clone iniziale), ma ora è disponibile la funzionalità di base su cui costruire interfacce più semplici.

Questo sembra molto più semplice:

git archive --remote=<repo_url> <branch> <path> | tar xvf -

Non è possibile clonare la sottodirectory solo con Git, ma di seguito sono riportate alcune soluzioni alternative.

Filiale filtro

Potresti voler riscrivere il repository per sembrare che trunk / public_html / sia stato la sua radice del progetto e scartare tutta la cronologia (usando filtro-ramo ), prova già sul ramo di checkout:

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

Note: Il - che separa le opzioni di filtro-ramo dalle opzioni di revisione e il --all per riscrivere tutti i rami e i tag. Tutte le informazioni, inclusi i tempi di commit originali o le informazioni di unione, verranno conservate . Questo comando onora il file .git / info / innts e fa riferimento allo spazio dei nomi refs / replace / , quindi se hai innesti o sostituzioni refs definito, l'esecuzione di questo comando li renderà permanenti.

  

Attenzione! La cronologia riscritta avrà nomi di oggetti diversi per tutti gli oggetti e non convergeranno con il ramo originale. Non sarà possibile spingere e distribuire facilmente il ramo riscritto sopra il ramo originale. Si prega di non utilizzare questo comando se non si conoscono le implicazioni complete ed evitare comunque di utilizzarlo, se fosse sufficiente un singolo commit per risolvere il problema.


Pagamento sparso

Ecco alcuni semplici passaggi con l'approccio checkout sparso che popolerà scarsamente la directory di lavoro, così puoi dì a Git quali cartelle o file nella directory di lavoro vale la pena di verificare.

  1. Clona il repository come al solito ( --no-checkout è facoltativo):

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

    Puoi saltare questo passaggio, se il tuo repository è già stato clonato.

    Suggerimento: per repository di grandi dimensioni, considera clone superficiale ( --depth 1 ) per effettuare il checkout solo dell'ultima revisione o / e - single-branch solo.

  2. Abilita l'opzione sparseCheckout :

    git config core.sparseCheckout true
    
  3. Specifica le cartelle per il checkout sparse ( senza spazio alla fine):

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

    o modifica .git/info/sparse-checkout.

  4. Verifica la filiale (ad es. master ):

    git checkout master
    

Ora dovresti aver selezionato le cartelle nella tua directory corrente.

Puoi prendere in considerazione collegamenti simbolici se hai troppi livelli di directory o rami di filtro invece.


Ho appena scritto uno script per GitHub .

Utilizzo:

python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>

Ecco uno script di shell che ho scritto per il caso d'uso di una singola sottodirectory checkout sparsa

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

Questo clonerà una cartella specifica e rimuoverà tutta la cronologia non correlata ad essa.

git clone --single-branch -b {branch} git@github.com:{user}/{repo}.git
git filter-branch --subdirectory-filter {path/to/folder} HEAD
git remote remove origin
git remote add origin git@github.com:{user}/{new-repo}.git
git push -u origin master
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top