web-dev-qa-db-fra.com

'git stash apply' avec le mode interactif

J'ai une série de fichiers dans une cachette (stash{0}) et j'aimerais git applyjuste certaines parties/morceaux de ces fichiers (généralement appelés mode interactif ).

C'est possible?

J'ai vu qu'il est possible d'effectuer un

git stash save -p 'Stash name'

mais il ne semble pas possible de le faire

git stash apply -p 'Stash name'

Connaissez-vous un moyen d'y parvenir?

32
Kamafeather

C'est possible?

Oui, ça l'est!

git checkout -p stash@{0}

Où vous pouvez remplacer le 0 dans stash@{0} avec l'index de la réserve que vous souhaitez appliquer.

Utilisation git stash list et git show -p stash@{n} si vous ne savez pas quelle n est la réserve que vous souhaitez appliquer.

N'oubliez pas de git stash drop stash@{n} quand vous savez que vous n'avez plus besoin de cette réserve, puisque git checkout évidemment ne laissera pas tomber la cachette pour vous.

Pourquoi ça marche?

La clé est de réaliser que les stashes sont, par essence, références à commits tout comme les balises et les branches.

En effet, ils sont stockés dans .git/refs/stash, une ligne par hachage de stash.

Avertissements

Comme @ mgadda mentionné dans les commentaires ci-dessous, git checkout -p essaie d'appliquer toute la différence entre une validation et l'espace de travail actuel.

Dans le cas d'un stash git, si le stash que vous essayez d'appliquer a été fait contre un autre commit, alors git checkout -p stash@{n} essaiera d'appliquer de manière interactive toutes les différences entre la validation stash@{n} et la validation de l'espace de travail actuel, , y compris toutes les validations parentes qui sont différentes .

Par exemple, si vous essayez d'appliquer une cachette qui a été enregistrée "il y a plusieurs validations" dans l'espace de travail actuel, git checkout -p stash@{n} essaiera d'appliquer non seulement les changements dans la cachette proprement dite, mais essaiera également de annuler toutes les modifications intervenues entre la validation sur laquelle la cachette est basé et le commit actuel.

Inversement, si vous essayez d'appliquer une cachette "du futur", c'est-à-dire dans une branche qui est un certain nombre de validations antérieures à la validation sur laquelle la cachette est basée, alors git checkout -p stash@{n} essaiera également d'appliquer toutes les autres modifications survenues entre la validation en cours et la validation à partir du futur, en plus des modifications de la réserve elle-même.

(Au cas où vous vous poseriez la question, git checkout -p stash@{n} une cachette d'une branche parallèle essaiera d'annuler toutes les modifications entre la validation en cours et le point de branchement d'origine et appliquera également toutes les modifications entre le point de branchement et l'autre branche, outre le changement de la cachette).

Solutions de contournement

Il existe quelques solutions de contournement, aucune d'entre elles n'est parfaite pour chaque situation:

    1. Soyez très prudent avec les correctifs que vous acceptez lorsque vous le faites git checkout -p stash@{n}
    1. Fait une git stash pop, puis git stash encore une fois avant de faire git checkout -p .... Mais si vous vouliez faire une application partielle de votre cachette pour éviter les conflits, cela n'aidera pas vraiment. Dans ce cas, voir la solution 4 ci-dessous.
    1. Si vous avez un outil graphique diff supporté par git (comme meld ), vous pouvez utiliser git difftool et "appliquer à gauche" uniquement les modifications qui vous intéressent:

      • git difftool -d stash@{n} pour comparer une stash entière et tous ses fichiers

      • git difftool stash@{n} -- path/to/file pour comparer un seul fichier

    1. (Basé sur la réponse de @ andrew) Sur une tête détachée, revenez au commit "parent" de la cachette qui vous intéresse, appliquez la cachette, re-stash interactivement seulement les parties qui vous sont intéressé, revenir en arrière et réappliquer la plus petite cachette.

Pas à pas:

git checkout stash@{n}^  # notice the "^". 

# Now you're in a detached head in the parent commit of the stash.
# It can be applied cleanly:
git stash apply stash@{n}

# Now save only the diffs you're interested in:
git stash -p

# remove the rest of the old stash
git checkout -- .  # be careful or you could remove unrelated changes

# go back to the branch where you want to apply the smaller stash
git checkout <my previous branch>

# apply the smaller stash
git stash pop
46
LeoRochael

Une façon possible est de réinitialiser l'index, puis d'utiliser ajout interactif

# 0. ensure there are no uncommitted changes
git status

# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any

# 2. reset the index 
git reset

# 3. interactively add the required chunks (except new files)
git add -p

# 4. stash all other changes
git stash save --keep-index "comment"
# 4. or just discards all other changes in the working tree
git checkout-index -f -a

# 5. commit
git commit -m "comment"

Une autre façon consiste à utiliser réinitialisation interactive à la place de l'ajout interactif.

# 0. ensure the working tree does not have unstaged changes
git status

# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any

# 2. interactively exclude the unneeded chunks from the index 
git reset -p

# 3. stash all other changes
git stash save --keep-index "comment"
# 3. or just discards all other changes in the working tree
git checkout-index -f -a

# 4. commit
git commit -m "comment"
2
ruvim

Je ne pense pas qu'il existe un moyen d'appliquer les modifications par mecs (ou même par fichier). Vous devrez appliquer le stash, puis stash les modifications que vous ne voulez pas de manière interactive (avec git stash save -p). Si vous êtes préoccupé par les conflits, vous pouvez d'abord masquer toutes les modifications non validées, appliquer votre cachette, cacher toutes les parties en conflit, puis appliquer l'autre cachette.

2
Andrew