Comment réinitialiser -- dur un sous-répertoire?

UPDATE : cela fonctionnera plus intuitivement à partir de Git 1.8.3, voir ma propre réponse .

imaginez le cas d'utilisation suivant: je veux me débarrasser de toutes les modifications dans un sous-répertoire spécifique de mon arbre de travail Git, en laissant tous les autres sous-répertoires intacts.

  • je peux le faire git checkout . , mais git checkout . ajoute des répertoires exclus par caisse légère

  • il y a git reset --hard , mais il ne me laissera pas le faire pour un sous-répertoire:

    > git reset --hard .
    fatal: Cannot do hard reset with paths.
    

    encore une fois: pourquoi git ne peut pas faire de réinitialisation dure/douce par le chemin?

  • je peux Inverser-patch l'état actuel en utilisant git diff subdir | patch -p1 -R , mais c'est une façon assez étrange de faire cela.

Quelle est la commande git appropriée pour cette opération?

Le script ci-dessous illustre le problème. Insérez la commande appropriée sous le commentaire How to make files -- la commande courante restaurera le fichier a/c/ac qui est supposé être exclu par la caisse clairsemée. Notez que je ne veut pas restaurer explicitement a/a et a/b , Je ne" sais " a et je veux restaurer tout ci-dessous. Modifier : et je ne sais pas non plus b , ou quels autres répertoires résident au même niveau que a .

#!/bin/sh

rm -rf repo; git init repo; cd repo
for f in a b; do
  for g in a b c; do
    mkdir -p $f/$g
    touch $f/$g/$f$g
    git add $f/$g
    git commit -m "added $f/$g"
  done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f

rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status

# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a

echo "After checkout:"
git status
find * -type f
129
demandé sur Community 2013-03-14 12:40:35

8 réponses

Note , a commenté par Dan Fabulich ) que:

  • git checkout -- <path> ne fait pas de réinitialisation dure: il remplace le contenu de l'arbre de travail par le contenu mis en scène.
  • git checkout HEAD -- <path> effectue un réarmement dur pour un chemin, en remplaçant à la fois l'index et l'arbre de travail par la version de la propagation HEAD .

Comme , a répondu par Ajedi32 , les deux formulaires de paiement ne suppriment pas les fichiers qui ont été supprimés dans la révision cible .

Si vous avez des fichiers supplémentaires dans L'arbre de travail qui n'existent pas dans HEAD, un git checkout HEAD -- <path> ne les supprimera pas.

mais ce checkout peut respecter un git update-index --skip-worktree (pour les répertoires que vous voulez ignorer), comme indiqué dans " pourquoi les fichiers exclus continuent à réapparaître dans mon git sparse checkout? ".

92
répondu VonC 2017-05-23 11:33:26

selon le développeur Git Duy Nguyen qui a aimablement mis en œuvre la fonctionnalité et un commutateur de compatibilité , les travaux suivants comme prévu à partir de Git 1.8.3 :

git checkout -- a

(où a est le répertoire que vous voulez réinitialiser). Le comportement original est accessible via

git checkout --ignore-skip-worktree-bits -- a
84
répondu krlmlr 2018-07-05 15:53:45

Essayez de changer

git checkout -- a

à

git checkout -- `git ls-files -m -- a`

depuis la version 1.7.0, ls-files honore le drapeau skip-worktree .

exécution de votre script de test (avec quelques modifications mineures git commit ... à git commit -q et git status à git status --short ) sorties:

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/c/ac
a/b/ab
b/a/ba

exécuter votre script de test avec le extrants de changement proposés checkout :

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/b/ab
b/a/ba
23
répondu Dan Cruz 2013-03-16 22:33:51

pour le cas de simplement rejeter des changements, les commandes git checkout -- path/ ou git checkout HEAD -- path/ suggérées par d'autres réponses fonctionnent très bien. Cependant, lorsque vous souhaitez réinitialiser un répertoire dans une révision autre que HEAD, cette solution a un problème important: elle ne supprime pas les fichiers qui ont été supprimés dans la révision cible.

donc à la place, j'ai commencé à utiliser la commande suivante:

git diff --cached commit -- subdir | git apply -R --index

cela fonctionne en trouvant la différence entre la propagation cible et l'index, puis en appliquant cette différence à l'envers au répertoire de travail et à l'index. Fondamentalement, cela signifie qu'il rend le contenu de l'index en fonction du contenu de la révision que vous avez spécifié. Le fait que git diff prend un argument path vous permet de limiter cet effet à un fichier ou répertoire spécifique.

puisque cette commande est assez longue et que je prévois de l'utiliser fréquemment, j'ai créé un alias pour elle que j'ai nommé reset-checkout :

git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'

Vous pouvez l'utiliser comme ceci:

git reset-checkout 451a9a4 -- path/to/directory

Ou simplement:

git reset-checkout 451a9a4
15
répondu Ajedi32 2015-01-16 16:45:17

Une réinitialisation normalement tout changer, mais vous pouvez utiliser git stash pour choisir ce que vous souhaitez conserver. Comme vous l'avez mentionné, stash n'accepte pas un chemin directement, mais il peut encore être utilisé pour garder un chemin spécifique avec le drapeau --keep-index . Dans votre exemple, vous planquerez le répertoire b, puis réinitialiserez tout le reste.

# How to make files a/* reappear without changing b and without recreating a/c?
git add b               #add the directory you want to keep
git stash --keep-index  #stash anything that isn't added
git reset               #unstage the b directory
git stash drop          #clean up the stash (optional)

cela vous amène à un point où la dernière partie de votre script affichera ceci:

After checkout:
# On branch master
# Changes not staged for commit:
#
#   modified:   b/a/ba
#
no changes added to commit (use "git add" and/or "git commit -a")
a/a/aa
a/b/ab
b/a/ba

je crois que c'était le résultat visé (b reste modifié, les fichiers A/* sont de retour, a/c n'est pas recréé).

Cette approche a l'avantage d'être très flexible; vous pouvez obtenir aussi fine que vous souhaitez ajouter des fichiers spécifiques, mais pas d'autres, dans un répertoire.

4
répondu Jonathan Wren 2013-03-17 01:37:22

si la taille du sous-répertoire n'est pas particulièrement énorme, et vous souhaitez rester à l'écart du CLI, voici une solution rapide à manuellement réinitialiser le sous-répertoire:

  1. passez à la branche principale et copiez le sous-répertoire à réinitialiser.
  2. revenez maintenant à votre branche principale et remplacez le sous-répertoire par la copie que vous venez de créer à l'étape 1.
  3. valider les modifications.

santé. Vous venez de réinitialiser manuellement un sous-répertoire dans votre branche de fonctionnalité pour être le même que celui de la branche principale !!

2
répondu Mehul Parmar 2017-10-16 17:24:12

Ajedi32 's réponse est ce que je cherchais, mais pour certains commet je suis tombé sur cette erreur:

error: cannot apply binary patch to 'path/to/directory' without full index line

Peut-être parce que certains fichiers du répertoire sont des fichiers binaires. L'ajout de l'option "--binary" à la commande git diff l'a corrigé:

git diff --binary --cached commit -- path/to/directory | git apply -R --index
1
répondu khelkun 2017-05-23 11:33:26

je vais offrir une option terrible ici, puisque je n'ai aucune idée comment faire quoi que ce soit avec git sauf add commit et push , voici comment j'ai "révéré" un sous-répertoire:

j'ai démarré un nouveau dépôt sur mon pc local, j'ai tout retourné à la propagation à partir de laquelle je voulais copier du code, puis j'ai copié ces fichiers dans mon répertoire de travail ., add commit push et voilà. Ne déteste pas le joueur, déteste M. Torvalds pour être plus intelligent que nous tous.

1
répondu Abraham Brookes 2018-09-13 23:21:27