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?
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 .
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.
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
.
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.
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.
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.
ce semble beaucoup plus simple:
git archive --remote=<repo_url> <branch> <path> | tar xvf -
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.
-
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. -
activer
sparseCheckout
option:git config core.sparseCheckout true
-
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
. -
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.
je viens de , a écrit un script pour GitHub .
Utilisation:
python get_git_sub_dir.py path/to/sub/dir <RECURSIVE>
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
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:
- https://github.com/git/git/blob/v2.19.0/Documentation/technical/partial-clone.txt
- https://github.com/git/git/blob/v2.19.0/Documentation/rev-list-options.txt#L720
- https://github.com/git/git/blob/v2.19.0/t/t5616-partial-clone.sh
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
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.