Vérifier les sous-répertoires dans Git?


160

Est-il possible d'extraire les sous-répertoires d'un référentiel dans Git?

Imaginez que je mets en place une nouvelle installation WordPress. Je vais créer deux nouveaux répertoires pour ma personnalisation de plugin et de thème:

  • wordpress/wp-content/plugins/myplugins/
  • wordpress/wp-content/themes/mytheme/

Je souhaite maintenir ces répertoires via Git. Dans Subversion, j'accomplir cela en ayant trunk/myplugins/et trunk/mytheme/répertoires et vérifier les sous - répertoires. Git a-t-il un moyen d'accomplir la même tâche en utilisant un seul référentiel?

Je pourrais juste manquer le bateau sur un paradigme Git, en tant qu'utilisateur de SVN de longue date avec peu d'exposition à Git.

Edit: plusieurs branches stockant un contenu différent est un moyen intéressant de gérer cela.


2
pourquoi ne pas extraire tout le dépôt et créer un lien symbolique vers les sous-répertoires avec lesquels vous souhaitez travailler?
randomness2077

4
duplication possible de Existe
MariuszS


Est-il possible d'effectuer une extraction fragmentée et de référencer le référentiel Git?
luka5z

Réponses:


121

Les extractions éparses sont maintenant dans Git 1.7 .

Voir également la question « Est-il possible d'effectuer une extraction clairsemée sans avoir d'abord vérifié l'ensemble du référentiel? ».

Notez que les extractions éparses vous obligent toujours à télécharger l'intégralité du référentiel, même si certains des fichiers téléchargés par Git ne se retrouveront pas dans votre arbre de travail.


1
Où est la git clonesimple commande ?? Eh bien, j'utilise cette réponse , ça marche!
Peter Krauss

4
Et y a-t-il un moyen de renommer ces dossiers? Si je passe à la caisse /foo/bar/foobar, est-il possible de le voir uniquement comme /foobardans mon référentiel local?
graywolf

17

Il n'y a pas vraiment de moyen de faire cela dans git. Et si vous n'effectuez pas de modifications qui affectent les deux arbres à la fois en tant qu'unité de travail unique, il n'y a aucune bonne raison d'utiliser un seul référentiel pour les deux. Je pensais que cette fonctionnalité de Subversion me manquerait, mais j'ai trouvé que la création de référentiels a si peu de frais administratifs mentaux (simplement en raison du fait que les référentiels sont stockés juste à côté de leur copie de travail, plutôt que de m'obliger à choisir explicitement un endroit en dehors du copie de travail) que je me suis habitué à créer de nombreux petits référentiels à usage unique.

Si vous insistez (ou en avez vraiment besoin), vous pouvez créer un référentiel git avec just mythemeet mypluginsrépertoires et créer un lien symbolique avec ceux-ci depuis l'installation de WordPress.


MDCore a écrit:

faire un commit, par exemple, mytheme incrémentera le numéro de révision de myplugin

Notez que ce n'est pas un problème pour git, si vous décidez de placer les deux répertoires dans un seul référentiel, car git supprime entièrement le concept d'augmentation monotone des numéros de révision de toute forme.

Le seul critère pour ce que les choses à mettre ensemble dans un seul référentiel dans git est de savoir s'il constitue une seule unité, c'est-à-dire. dans votre cas, s'il y a des changements pour lesquels il n'est pas logique d'examiner les modifications dans chaque répertoire de manière isolée. Si vous avez des modifications pour lesquelles vous devez éditer des fichiers dans les deux répertoires à la fois et que les modifications vont ensemble, elles doivent constituer un seul référentiel. Sinon, ne les glissez pas ensemble.

Git veut vraiment que vous utilisiez des référentiels séparés pour des entités séparées.

sous-modules

Les sous - modules ne traitent pas le désir de garder les deux répertoires dans un référentiel, parce qu'ils seraient effectivement mettre en vigueur ayant un dépôt distinct pour chaque répertoire, qui sont ensuite réunis dans un autre référentiel à l' aide des sous - modules. Pire encore, étant donné que les répertoires à l'intérieur de l'installation de WordPress ne sont pas des sous-répertoires directs du même répertoire et font également partie d'une hiérarchie avec de nombreux autres fichiers, l'utilisation des référentiels par répertoire comme sous-modules dans un référentiel unifié n'offrirait aucun avantage, car l'unifié le référentiel ne refléterait aucun cas d'utilisation / besoin.


Où est la git clonesimple séquence de commandes ?? Eh bien, j'utilise cette réponse , ça marche!
Peter Krauss

16

Une chose que je n'aime pas dans les extractions éparses, c'est que si vous voulez extraire un sous-répertoire de quelques répertoires de profondeur, votre structure de répertoires doit contenir tous les répertoires qui y mènent.

La façon de contourner ce problème consiste à cloner le référentiel dans un endroit qui n'est pas mon espace de travail, puis à créer un lien symbolique dans le répertoire de mon espace de travail vers le sous-répertoire du référentiel. Git fonctionne très bien comme ça car des choses comme git status afficheront les fichiers de changement par rapport à votre répertoire de travail actuel.


Cela ne fonctionne que dans un système d'exploitation prenant en charge les liens symboliques. Ils doivent changer le fonctionnement des caisses clairsemées.
Anders Lindén

1
+1 pour l'idée avec un lien symbolique sur le répertoire extrait. Cependant, une extraction éparse et un lien symbolique ne s'excluent pas mutuellement: vous n'avez pas besoin d'un clone à part entière.
apitsch le

10

En fait, les extractions "étroites", "partielles" ou "éparses" sont en cours de développement intensif pour Git. Notez que vous aurez toujours le référentiel complet sous .git. Donc, les deux autres articles sont à jour pour l'état actuel de Git, mais il semble que nous serons en mesure de faire des vérifications éparses éventuellement. Consultez les listes de diffusion si vous êtes intéressé par plus de détails - elles changent rapidement.


Bon à savoir! J'aime avoir des répertoires aussi étroitement liés sous un seul référentiel, et je le ferais si possible.
Annika Backstrom

6

git clone --filter à partir de Git 2.19

Cette option ignorera en fait la récupération des 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 -- mdir/

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 la v2.19.0, mais il peut déjà être testé localement.

file://$(path)est nécessaire pour surmonter git cloneles manigances de protocole: comment cloner superficiellement un référentiel git local avec un chemin relatif?

Rappelez-vous que cela --depth 1implique déjà --single-branch, voir aussi: Comment cloner une seule branche dans Git?

TODO: --filter=blob:noneignore tous les objets blob, mais récupère toujours tous les objets de l'arborescence. Mais sur un dépôt normal, cela devrait être minuscule par rapport aux fichiers eux-mêmes, donc c'est déjà assez bon. Interrogé sur: https://www.spinics.net/lists/git/msg342006.html Les développeurs ont répondu un--filter=tree:0 était en train de le faire.

Le format de --filterest documenté sur man git-rev-list.

Une extension a été apportée au protocole distant Git pour prendre en charge cette fonctionnalité.

Docs sur l'arbre Git:

Testez-le

#!/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 dans Git v2.19:

# 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 extérieurs à d1/sont manquants.

Notez que root/rootet mybranch/mybranchsont également manquants, mais --depth 1masque cela dans la liste des fichiers manquants. Si vous supprimez --depth 1, ils apparaissent dans la liste des fichiers manquants.


1

Comme votre édition le souligne, vous pouvez utiliser deux branches distinctes pour stocker les deux répertoires séparés. Cela les garde tous les deux dans le même référentiel, mais vous ne pouvez toujours pas avoir de commits couvrant les deux arborescences de répertoires. Si vous avez un changement dans l'un qui nécessite un changement dans l'autre, vous devrez le faire en tant que deux commits distincts, et vous ouvrirez la possibilité qu'une paire d'extraction des deux répertoires puisse se désynchroniser.

Si vous voulez traiter la paire de répertoires comme une seule unité, vous pouvez utiliser 'wordpress / wp-content' comme racine de votre dépôt et utiliser le fichier .gitignore au niveau supérieur pour ignorer tout sauf les deux sous-répertoires d'intérêt. C'est probablement la solution la plus raisonnable à ce stade.

Des contrôles clairsemés arriveraient depuis deux ans maintenant, mais il n'y a toujours aucun signe d'eux dans le dépôt de développement git, ni aucune indication que les changements nécessaires y arriveront un jour. Je ne compterais pas sur eux.


1

Vous ne pouvez pas extraire un seul répertoire d'un référentiel car l'ensemble du référentiel est géré par le dossier .git unique à la racine du projet au lieu de la myriade de répertoires .svn de Subversion.

Le problème avec le travail sur des plugins dans un référentiel unique est que faire un commit sur, par exemple, mytheme incrémentera le numéro de révision de myplugin , donc même en subversion, il est préférable d'utiliser des référentiels séparés.

Le paradigme de subversion pour les sous-projets est svn: externals qui se traduit quelque peu en sous-modules dans git (mais pas exactement au cas où vous auriez déjà utilisé svn: externals).


0

Il y a une inspiration ici. Utilisez simplement shell regexou git regex.

git checkout commit_id */*.bat  # *.bat in 1-depth subdir exclude current dir, shell regex  
git checkout commit_id '*.bat'  # *.bat in all subdir include current dir, git regex

Utilisez les guillemets pour échapper à l'interprétation des expressions régulières du shell et passez des caractères génériques à git.

Le premier n'est pas récursif, uniquement des fichiers en profondeur 1 subdir. Mais le second est récursif.

Quant à votre situation, ce qui suit peut suffire.

git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'

Piratez simplement les lignes selon vos besoins.


0

Vous ne pouvez annuler les modifications non validées que dans un fichier ou un répertoire particulier:

git checkout [some_dir|file.txt]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.