Comment cloner un sous-répertoire d'un dépôt Git?

j'ai mon dépôt Git qui, à la racine, a deux sous-répertoires:

/finisht
/static

quand il était dans SVN , /finisht a été vérifié dans un endroit, tandis que /static a été vérifié ailleurs, comme ainsi:

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

y a-t-il un moyen de faire ça avec Git?

1047
demandé sur Peter Mortensen 2009-03-01 19:46:32

11 réponses

non, ce n'est pas possible en Git.

la mise en œuvre d'un tel système dans le cadre de L'initiative de Git représenterait un effort considérable et signifierait que l'intégrité du dépôt côté client ne pourrait plus être garantie. Si vous êtes intéressé, cherchez des discussions sur "sparse clone" et "sparse fetch" sur la liste de diffusion git.

en général, le consensus dans la communauté Git est que si vous avez plusieurs répertoires qui sont toujours cochés de façon indépendante, ce sont vraiment deux projets différents et devraient vivre dans deux dépôts différents. Vous pouvez les recoller ensemble en utilisant Git Submodules .

429
répondu Jörg W Mittag 2018-08-31 14:51:53

Ce que vous essayez de faire est appelée un sparse checkout , et que la fonctionnalité a été ajoutée dans git 1.7.0 (Fév. 2012). Les étapes pour faire un sparse clone sont les suivantes:

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

cela crée un dépôt vide avec votre télécommande, et récupère tous les objets mais ne les vérifie pas. Puis faire:

git config core.sparseCheckout true

Maintenant vous devez définir quels fichiers/dossiers vous voulez réellement vérifier. Ceci est fait en les énumérant dans .git/info/sparse-checkout , par exemple:

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

Last mais pas moins, de mettre à jour votre vide des pensions de l'état à partir de la télécommande:

git pull origin master

vous aurez maintenant des fichiers" checked out "pour some/dir et another/sub/tree sur votre système de fichiers (avec ces chemins toujours), et aucun autre chemin présent.

vous pourriez vouloir jeter un oeil à la tutoriel étendu et vous devriez probablement lire la documentation officielle pour la caisse clairsemée .

en fonction:

function git_sparse_clone() (
  rurl="" localdir="" && 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
)

Utilisation:

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

notez que cela va tout de même télécharger le dépôt entier à partir du serveur – seule la caisse est réduite en taille. Pour le moment, il n'est pas possible de cloner un seul répertoire. Mais si vous n'avez pas besoin de l'histoire du référentiel, vous pouvez sauver au moins sur la bande passante en créant un clone superficiel. Voir udondan's answer ci-dessous pour obtenir de l'information sur la façon de combiner la caisse peu profonde clone et la caisse clairsemée.

1392
répondu Chronial 2018-04-03 14:05:35

vous pouvez combiner les caractéristiques caisse clairsemée et le clone peu profond . Le clone peu profond coupe l'histoire et le caisse clairsemée ne tire les fichiers correspondant à vos motifs.

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

vous aurez besoin d'un minimum de git 1.9 pour que cela fonctionne. Testé moi-même qu'avec 2.2.0 et 2.2.2.

de cette façon, vous serez encore en mesure de push , ce qui n'est pas possible avec git archive .

341
répondu udondan 2015-02-25 00:22:18

pour les autres utilisateurs qui simplement pour télécharger un fichier/dossier github, simplement utiliser:

svn export <repo>/trunk/<folder>

par exemple

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

(oui, c'est svn ici. apparemment en 2016, vous avez encore besoin de svn pour simplement télécharger quelques fichiers github)

courtoisie: télécharger un seul dossier ou répertoire à partir d'un GitHub repo

Important - assurez-vous de mettre à jour L'URL github et de remplacer /tree/master/ par " /trunk/".

Comme script bash:

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

Note Cette méthode télécharge un dossier, ne clone pas/checkout il. Vous ne pouvez pas renvoyer les changements au dépôt. D'autre part - cela résulte en un téléchargement plus petit par rapport à la caisse clairsemée ou peu profonde.

90
répondu Anona112 2018-06-18 11:32:16

Git 1.7.0 a"des caisses rares". Voir "core.sparseCheckout" in the git config manpage , "Sparse " caisse" du git lecture de l'arbre manpage , et "Ignorez-worktree bits" dans le git update-index manpage .

l'interface n'est pas aussi commode que celle de SVN (par exemple, il n'y a aucun moyen de faire une vérification éparse au moment de un clone initial), mais la fonctionnalité de base sur laquelle des interfaces plus simples pourraient être construites est maintenant disponible.

63
répondu Chris Johnsen 2012-06-06 04:42:07

si vous ne prévoyez jamais d'interagir avec le dépôt à partir duquel vous avez cloné, vous pouvez faire un clone git et réécrire votre dépôt en utilisant filtre git-branche --sous-répertoire-filtre . De cette façon, au moins l'histoire sera préservée.

63
répondu hillu 2015-06-14 09:09:34

ce semble beaucoup plus simple:

git archive --remote=<repo_url> <branch> <path> | tar xvf -
60
répondu ErichBSchulz 2016-09-02 19:22:44

il n'est pas possible de cloner un sous-répertoire seulement avec Git, mais ci-dessous il y a peu de solutions de rechange.

branche filtre

vous pourriez vouloir réécrire le dépôt pour avoir l'air comme si trunk/public_html/ avait été sa racine de projet, et rejeter toute autre histoire (en utilisant filter-branch ), essayez déjà la branche caisse:

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

Notes: Le -- qui sépare la branche filtrante options des options de révision, et le --all pour réécrire toutes les branches et étiquettes. Toutes les informations incluant les temps de propagation originaux ou les informations de fusion seront préservé . Cette commande honore le fichier .git/info/grafts et les références dans l'espace de nom refs/replace/ , donc si vous avez des grafts ou des remplacements refs définis, l'exécution de cette commande les rendra permanents.

attention! L'histoire réécrite aura des noms d'objets différents pour tous les objets et ne convergeront pas avec la branche originale. Vous ne pourrez pas facilement pousser et distribuer la branche réécrite sur la branche originale. S'il vous plaît, n'utilisez pas cette commande si vous ne connaissez pas toutes les implications, et évitez de l'utiliser de toute façon, si un simple commit suffit pour résoudre votre problème.


Sparse checkout

Voici des étapes simples avec caisse clairsemée approche qui peuplera le répertoire de travail par endroits, de sorte que vous pouvez dire à Git quel(S) dossier(s) ou fichier (s) dans le répertoire de travail sont à vérifier.

  1. dépôt Clone comme d'habitude ( --no-checkout est facultatif):

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

    vous pouvez sauter cette étape, si vous avez déjà votre dépôt cloné.

    Indice: Pour grandes pensions, considérez clone peu profond ( --depth 1 ) pour vérifier seulement la dernière révision ou/et --single-branch seulement.

  2. activer sparseCheckout option:

    git config core.sparseCheckout true
    
  3. spécifier le(S) dossier (s) pour la caisse clairsemée ( sans espace à la fin):

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

    ou "modifier .git/info/sparse-checkout .

  4. vérification de la succursale (p.ex. master ):

    git checkout master
    

Maintenant, vous auriez dû sélectionner des dossiers dans votre répertoire courant.

vous pouvez considérer les liens symboliques si vous avez trop de niveaux de répertoires ou de branche de filtrage à la place.


23
répondu kenorb 2016-07-21 22:12:12

je viens de , a écrit un script pour GitHub .

Utilisation:

python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>
11
répondu david_adler 2016-09-02 19:22:28

voici un script shell que j'ai écrit pour le cas d'utilisation d'un seul sous-répertoire sparse checkout

coSubDir.sh

localRepo=
remoteRepo=
subDir=


# 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
4
répondu jxramos 2018-03-08 19:39:46

git clone --filter de Git 2.19

cette option ne récupérera pas les objets inutiles du serveur:

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

le serveur doit être configuré avec:

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

il n'y a pas de support de serveur à partir de v2.19.0, mais il peut déjà être testé localement.

TODO: --filter=blob:none saute toutes les taches, mais va chercher tous les objets de l'arbre. Mais sur un normal repo, cela devrait être minuscule comparé aux fichiers eux-mêmes, donc c'est déjà assez bon. Demandé à: https://www.spinics.net/lists/git/msg342006.html Devs a répondu un --filter=tree:0 est dans les travaux pour le faire.

rappelez-vous que --depth 1 implique déjà --single-branch , voir aussi: comment cloner une seule branche en git?

file://$(path) est nécessaire pour surmonter les git clone protocole shenanigans: comment cloner superficiellement un dépôt git local avec un chemin relatif?

le format de --filter est documenté sur man git-rev-list .

Docs sur l'arbre Git:

Tester

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

sortie en 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

Conclusions: tous les blobs de l'extérieur de d1/ sont manquants. Par exemple: 0975df9b39e23c15f63db194df7f45c76528bccb , qui est d2/b n'est pas là après avoir coché d1/a .

Note que root/root et mybranch/mybranch sont également absents, mais --depth 1 cache qu'à partir de la liste des fichiers manquants. Si vous supprimez --depth 1 , alors ils apparaissent sur la liste des fichiers manquants.

1