UPDATE² : Avec Git 2.23 (août 2019), il y a une nouvelle commande
git restore
qui fait cela, voir la réponse acceptée .MISE À JOUR : 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 une extraction clairseméeIl y en a
git reset --hard
, mais ça 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 effectuer de réinitialisations matérielles / logicielles par chemin d'accès?
Je peux inverser la correction de l'état actuel en utilisant
git diff subdir | patch -p1 -R
, mais c'est une façon plutôt étrange de le faire.
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 How to make files
commentaire - la commande en cours restaurera le fichier a/c/ac
qui est censé être exclu par la vérification clairsemée. Notez que je ne veux pas restaurer explicitement a/a
et a/b
, je "connais" seulement et je a
veux tout restaurer ci-dessous. EDIT : Et je ne sais pas non plus b
, ni 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
la source
git stash && git stash drop
?git checkout -- /path/to/subdir/
?git stash
n'accepte pas d'argument de chemin ...Réponses:
Avec Git 2.23 (août 2019), vous avez la nouvelle commande
git restore
Cela remplacerait à la fois l'index et l'arborescence de travail par du
HEAD
contenu, comme lereset --hard
ferait, mais pour un chemin spécifique.Réponse originale (2013)
Note (comme commenté par Dan Fabulich ) que:
git checkout -- <path>
ne fait pas de réinitialisation matérielle: il remplace le contenu de l'arborescence de travail par le contenu intermédiaire.git checkout HEAD -- <path>
effectue une réinitialisation matérielle pour un chemin d'accès, en remplaçant à la fois l'index et l'arborescence de travail par la version duHEAD
commit.Comme répondu par Ajedi32 , les deux formes de caisse ne suppriment pas les fichiers qui ont été supprimés dans la révision cible .
Si vous avez des fichiers supplémentaires dans l'arborescence de travail qui n'existent pas dans HEAD, a
git checkout HEAD -- <path>
ne les supprimera pas.Remarque: Avec
git checkout --overlay HEAD -- <path>
(Git 2.22, Q1 2019) , les fichiers qui apparaissent dans l'index et l'arborescence de travail, mais pas dans<tree-ish>
sont supprimés, pour les faire correspondre<tree-ish>
exactement.Mais cette extraction peut respecter un
git update-index --skip-worktree
(pour les répertoires que vous souhaitez ignorer), comme indiqué dans « Pourquoi les fichiers exclus continuent-ils de réapparaître dans mon extraction git clairsemée? ».la source
git checkout HEAD -- .
, les fichiers exclus par une extraction clairsemée réapparaissent. Que faut-ilgit update-index --skip-worktree
faire?git checkout HEAD -- <path>
, puis de supprimer les répertoires qui ont été restaurés (mais qui sont toujours déclarés lors de l'extraction clairsemée).Selon le développeur Git Duy Nguyen qui a aimablement implémenté la fonctionnalité et un commutateur de compatibilité , les éléments suivants fonctionnent comme prévu à partir de Git 1.8.3 :
(où
a
est le répertoire que vous souhaitez réinitialiser). Le comportement d'origine est accessible viala source
git checkout -- .
où.
signifie le répertoire en cours.git reset -- a
(où a est le répertoire que vous souhaitez réinitialiser)git reset --hard
le repo entier?rm -rf a
avant.Essayez de changer
à
Depuis la version 1.7.0, Git's
ls-files
honore le drapeau skip-worktree .Exécution de votre script de test (avec quelques modifications mineures changeant
git commit
... versgit commit -q
etgit status
versgit status --short
) sorties:Exécution de votre script de test avec les
checkout
sorties de modification proposées :la source
git checkout
respecter le bit "skip-worktree" en premier lieu?checkout.c
ettree.c
ne révèle pas que l'indicateur skip-worktree est utilisé.Dans le cas d'une simple suppression des modifications, le
git checkout -- path/
ougit checkout HEAD -- path/
commandes 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.Au lieu de cela, j'ai commencé à utiliser la commande suivante:
Cela fonctionne en trouvant le diff entre le commit cible et l'index, puis en appliquant ce diff à l'envers au répertoire de travail et à l'index. Fondamentalement, cela signifie que le contenu de l'index correspond au contenu de la révision que vous avez spécifiée. Le fait que
git diff
prendre un argument chemin vous permet de limiter cet effet à un fichier ou un répertoire spécifique.Comme cette commande est assez longue et que je prévois de l'utiliser fréquemment, j'ai mis en place un alias que j'ai nommé
reset-checkout
:Vous pouvez l'utiliser comme ceci:
Ou juste:
la source
git checkout --overlay HEAD -- <path>
commande mentionnée par @VonC dans sa réponse?git checkout --overlay HEAD -- <path>
n'est pas encore sorti (Git 2.22 sera publié au T2 2019)Je vais proposer une terrible option ici, car je n'ai aucune idée de comment faire quoi que ce soit avec git sauf
add
commit
etpush
, voici comment j'ai "inversé" un sous-répertoire:J'ai commencé un nouveau référentiel sur mon PC local, rétabli le tout au commit à partir duquel je voulais copier le code, puis copié ces fichiers dans mon répertoire de travail,
add
commit
push
et le tour est joué. Ne détestez pas le joueur, détestez M. Torvalds pour être plus intelligent que nous tous.la source
Une réinitialisation changera normalement tout, mais vous pouvez utiliser
git stash
pour choisir ce que vous souhaitez conserver. Comme vous l'avez mentionné,stash
n'accepte pas directement un chemin, mais il peut toujours être utilisé pour conserver un chemin spécifique avec l'--keep-index
indicateur. Dans votre exemple, vous devez ranger le répertoire b, puis réinitialiser tout le reste.Cela vous amène à un point où la dernière partie de votre script affichera ceci:
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 supplémentaire d'être très flexible; vous pouvez obtenir autant de détails que vous le souhaitez en ajoutant des fichiers spécifiques, mais pas d'autres, dans un répertoire.
la source
git add
tout saufa
, non? Cela semble difficile dans la pratique.git add .
alorsgit reset a
tout ajouter saufa
.git add
n'ajoute pas de fichiers supprimés. Donc, si vous ne récupérez que des fichiers supprimés,git add .
tous les fichiers modifiés seront ajoutés, mais pas ceux supprimés.Si la taille du sous-répertoire n'est pas particulièrement énorme ET que vous souhaitez rester à l'écart de la CLI, voici une solution rapide pour réinitialiser manuellement le sous-répertoire:
À votre santé. Vous venez de réinitialiser manuellement un sous-répertoire dans votre branche de fonctionnalité pour qu'il soit identique à celui de la branche principale !!
la source
La réponse d' Ajedi32 est ce que je cherchais, mais pour certains commits, j'ai rencontré 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é:
la source
Qu'en est-il de
la source