GIT: extraction dans un dossier spécifique

128

Je veux utiliser quelque chose de similaire à:

git checkout -- <path>/<file>

mais je veux extraire le fichier dans un dossier de mon choix, plutôt que d'écraser le fichier local <path>/<file>.

Une idée?

Rafid
la source
2
Voir: stackoverflow.com/questions/160608/…
Adam Vandenberg

Réponses:

57

Comme pour Faire un "git export" (comme "svn export")?

Vous pouvez utiliser git checkout-indexpour cela, c'est une commande de bas niveau, si vous voulez tout exporter, vous pouvez utiliser -a,

git checkout-index -a -f --prefix=/destination/path/

Pour citer les pages de manuel:

Le "/" final [sur le préfixe] est important. Le nom exporté est littéralement précédé de la chaîne spécifiée.

Si vous souhaitez exporter un certain répertoire, il existe quelques astuces. La commande ne prend que des fichiers, pas des répertoires. Pour l'appliquer aux répertoires, utilisez la commande 'find' et dirigez la sortie vers git.

find dirname -print0 | git checkout-index --prefix=/path-to/dest/ -f -z --stdin

Également à partir des pages de manuel:

L'intuitivité n'est pas le but ici. La répétabilité est.

hasen
la source
Malheureusement, cela ne fonctionnera pas à partir d'un dépôt git nu, même avec l'ensemble --prefix :-( (testé avec git 1.9.1)
apeiros
1
si vous voulez extraire une branche / commit d'un dépôt git nu, utilisezGIT_WORK_TREE=../path/to/place git checkout
allicoder
4
git worktree(voir ci-dessous) à mon humble avis est la réponse canonique aujourd'hui et pourrait être ajoutée ici.
geek-merlin
213

Une autre solution qui est un peu plus propre - spécifiez simplement un arbre de travail différent.

Pour tout extraire de votre HEAD (pas d'index) vers un répertoire de sortie spécifique:

git --work-tree=/path/to/outputdir checkout HEAD -- .

Pour extraire un sous-répertoire ou un fichier de votre HEAD vers un répertoire spécifique:

git --work-tree=/path/to/outputdir checkout HEAD -- subdirname
Adrian Macneil
la source
4
Note mineure - vous avez besoin d'un chemin absolu car l'expansion du tilde du shell ne se produit pas, c'est-à-dire --work-tree=/home/thomasg/okcopyplutôt que --work-tree=~/okcopy(éventuellement utiliser un chemin relatif tout en étant assis dans le même arbre git fonctionne aussi, mais de cette façon réside la folie et les git statussorties en R'lyehian)
Tom Goodfellow
10
Cela fonctionne aussi comme prévu avec les anciens commits (par exemple, donnez un SHA à la place de HEAD), mais git statusaffiche alors beaucoup de mods (probablement parce que l'index correspond maintenant à l'autre répertoire et non à l'arbre de travail normal intact). git resetl'a remis en bon état.
Tom Goodfellow
2
La même chose ici git statusmontre beaucoup de mods et git resetn'aide pas. J'ai dû git checkout -f HEADrestaurer l'état de mon repo.
DUzun
11
FYI: vous devez créer le répertoire vous-même, sinon vous obtenez cette erreur:fatal: This operation must be run in a work tree
timaschew
13
Cela modifie de manière désastreuse l'index de l'arbre de travail principal, ce qui est généralement mauvais. Comme le suggère @TomGoodfellow, il git resetsuffit de restaurer l'arborescence de travail principale. Pour exporter en toute sécurité les sous-répertoires du référentiel dans n'importe quel SHA1, branche ou balise sans modifier l'arborescence de travail principale, consultez la tristement célèbre git archivesolution de Charles Bailey . De même, pour vérifier en toute sécurité plusieurs branches en même temps, la nouvelle git worktree addsous-commande est votre ami.
Cecil Curry
25

Pour un seul fichier:

git show HEAD:abspath/to/file > file.copy
Tobu
la source
2
+1 et pour clarifier le "certain commit précédent" (au lieu de HEAD): Il se réfère au SHA1 IDqui peut être facilement trouvé via gitk. Si je n'ai besoin que de «récupérer» ce fichier vers un emplacement temporaire (c'est-à-dire sans revenir en arrière), j'utiliserais la showsous - commande:git show 82e54378856215ef96c5db1ff1160a741b5dcd70:MyProj/proguard/mapping.txt > myproj_mapping.txt
ef2011
19

Les solutions ci-dessus n'ont pas fonctionné pour moi car je devais vérifier une version spécifique balisée de l'arborescence. C'est ainsi que l' cvs exporton entend être utilisé, d'ailleurs. git checkout-indexne prend pas l'argument tag, car il extrait les fichiers de l'index. git checkout <tag>changerait l'index quel que soit l'arbre de travail, donc je devrais réinitialiser l'arbre d'origine. La solution qui a fonctionné pour moi était de cloner le référentiel. Le clone partagé est assez rapide et ne prend pas beaucoup d'espace supplémentaire. Le .gitrépertoire peut être supprimé si vous le souhaitez.

git clone --shared --no-checkout <repository> <destination>
cd <destination>
git checkout <tag>
rm -rf .git

Les nouvelles versions de git devraient prendre git clone --branch <tag>en charge l'extraction automatique de la balise spécifiée:

git clone --shared --branch <tag> <repository> <destination>
rm -rf <destination>/.git
proski
la source
3
git --work-tree=/path/to/outputdir checkout <tag> -- .n'a pas fonctionné pour vous?
warvariuc
1
Non, ce n'est pas le cas. Le fichier réel dans le répertoire de travail d'origine est inchangé, mais la version intermédiaire est perdue et remplacée par la version de la balise. C'est particulièrement grave si la version intermédiaire contenait un ensemble de modifications soigneusement sélectionnées à valider. Je ne veux pas que la commande d'exportation touche mon index, point final.
proski
19

Si vous travaillez sous votre fonctionnalité et que vous ne souhaitez pas revenir à Master, vous pouvez exécuter:

cd ./myrepo

git worktree add ../myrepo_master master

git worktree remove ../myrepo_master

Il créera un ../myrepo_masterrépertoire avec des mastercommits de branche, où vous pourrez continuer à travailler

Itsnikolay
la source
1
Meilleure réponse - simple, et contrairement à git --work-tree=/path/to/outputdir checkout HEAD -- .cela ne fait rien à l'index, copie simplement la branche sélectionnée à l'emplacement spécifié (avec l'ajout d'un fichier .git).
Roger Dueck
Et comment annuler ce changement?
Scott P.
@ ScottP.just supprimer le myrepo_masterrépertoire
itsnikolay
1
annuler ceci est une sous-commande de worktree:git worktree remove ../myrepo_master
Martijn Dashorst
8

La réponse d'Adrian a jeté "fatal: Cette opération doit être exécutée dans un arbre de travail." Voici ce qui a fonctionné pour nous.

git worktree add <new-dir> --no-checkout --detach
cd <new-dir>
git checkout <some-ref> -- <existing-dir>

Remarques:

  • --no-checkout Ne retirez rien dans le nouvel arbre de travail.
  • --detach Ne créez pas de nouvelle branche pour le nouvel arbre de travail.
  • <some-ref>fonctionne avec n'importe quelle référence, par exemple, avec HEAD~1.
  • Nettoyage avec git worktree prune.
Shaun Luttin
la source
+1 pour avoir commenté le nettoyage - tout le monde est heureux de gâcher le dépôt actuel. seul celui-ci n'a pas d'effets secondaires.
Andrew Hill
1

Ajout à la réponse de @ hasen . Pour répertorier les fichiers à extraire , vous pouvez utiliser git ls-filesau lieu de findcomme:

git ls-files -z *.txt | git checkout-index --prefix=/path-to/dest/ -f -z --stdin
snipsnipsnip
la source
Merci de bien utiliser -z! Bien que vous fournissiez un script qui n'est pas vulnérable à certains types d'attaques par injection.
ErikE le
0

J'ai défini un alias git pour y parvenir (avant de trouver cette question).

C'est une courte fonction bash qui enregistre le chemin actuel, passe au repo git, effectue une extraction et retourne là où il a commencé.

git checkpour développer ~ / mon_projet_git

Cela permettrait par exemple d'extraire la branche de développement dans le répertoire "~ / my_project_git".

Voici le code d'alias à l'intérieur ~/.gitconfig:

[alias]
    checkTo = "!f(){ [ -z \"$1\" ] && echo \"Need to specify branch.\" && \
               exit 1; [ -z \"$2\" ] && echo \"Need to specify target\
               dir\" && exit 2; cDir=\"$(pwd)\"; cd \"$2\"; \
               git checkout \"$1\"; cd \"$cDir\"; };f"
cb0
la source
0

J'utilise cet alias pour extraire une branche dans un répertoire temporaire:

[alias]
    cot = "!TEMP=$(mktemp -d); f() { git worktree prune && git worktree add $TEMP $1 && zsh -c \"cd $TEMP; zsh\";}; f" # checkout branch in temporary directory

Usage:

git cot mybranch

Vous êtes ensuite déposé dans un nouveau shell dans le répertoire temporaire où vous pouvez travailler sur la branche. Vous pouvez même utiliser des commandes git dans ce répertoire.

Lorsque vous avez terminé, supprimez le répertoire et exécutez:

git worktree prune

Cela se fait également automatiquement dans l'alias, avant d'ajouter un nouvel arbre de travail.

fdietze
la source
0

Utilisez git archive branch-index | tar -x -C your-folder-on-PCpour cloner une branche dans un autre dossier. Je pense que vous pouvez alors copier n'importe quel fichier dont vous avez besoin

dqthe
la source