Ajouter uniquement des modifications non blanches

343

J'ai mon éditeur de texte pour couper automatiquement les espaces de fin lors de l'enregistrement d'un fichier, et je contribue à un projet open source qui a de graves problèmes avec les espaces de fin.

Chaque fois que j'essaie de soumettre un patch, je dois d'abord ignorer manuellement toutes les modifications uniquement sur les espaces, pour ne choisir que les informations pertinentes. Non seulement cela, mais lorsque je cours, git rebaseje rencontre généralement plusieurs problèmes à cause d'eux.

En tant que tel, je voudrais pouvoir ajouter à l'index uniquement des modifications non blanches, d'une manière similaire à celle- git add -pci, mais sans avoir à choisir moi-même toutes les modifications.

Est-ce que quelqu'un sait comment faire ça?

EDIT: Je ne peux pas changer la façon dont le projet fonctionne, et ils ont décidé, après en avoir discuté sur la liste de diffusion, de l'ignorer.

Edu Felipe
la source

Réponses:

395

La solution @Frew n'était pas tout à fait ce dont j'avais besoin, c'est donc l'alias que j'ai fait pour le même problème:

alias.addnw=!sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero -'

Ou vous pouvez simplement exécuter:

git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

Mise à jour

Ajout d'options -U0et --unidiff-zerorespectivement de contournement des problèmes de correspondance de contexte, selon ce commentaire .

Fondamentalement, il applique le patch qui serait appliqué addsans modification des espaces blancs. Vous remarquerez qu'après une git addnw your/filemodification, il y aura toujours des changements non programmés, ce sont les espaces laissés.

Le --no-color n'est pas requis mais comme j'ai des couleurs toujours définies, je dois l'utiliser. Quoi qu'il en soit, mieux vaut prévenir que guérir.

Colin Hebert
la source
7
Cela a bien fonctionné pour moi, mais j'ai dû utiliser git apply --ignore-whitespacesinon le patch ne s'appliquerait pas pour des raisons évidentes.
jupp0r
106
Il devrait vraiment y avoir une option pour git add, comme git add -wça l'a fait.
Jarl
7
Cela me pose des problèmes patch does not applyet error while searching for... Des idées?
DTI-Matt
18
n'a pas fonctionné pour moi. Vous avez une patch does not applyerreur.
Jerry Saravia
13
Si vous obtenez « correctif échoué » en raison de des espaces dans le contexte de points de @bronson, à la fonctionnement de cette commande révisée (Il génère un patch sans contexte): git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. Ce n'est pas risqué car l'index est déjà aussi à jour que possible, c'est donc une base fiable pour le patch.
void.pointer
36

Cela fonctionne pour moi:

Si vous voulez garder une cachette, cela fonctionne

git stash && git stash apply && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Je ne aime pas les stashes, mais je l' ai rencontré un bogue dans git + Cygwin où je perds des changements, afin de vous assurer que des choses est allé au reflog au moins je définir les paramètres suivants:

git add . && git commit -am 'tmp' && git reset HEAD^ && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

Fondamentalement, nous créons un diff qui n'inclut pas les changements d'espace, annulons tous nos changements, puis appliquons le diff.

Frew Schmidt
la source
1
+1. Vous voudrez peut-être faire un git stashau lieu de payer, pour avoir une sauvegarde de vos modifications, au moins jusqu'à ce qu'il soit testé.
Paŭlo Ebermann
1
Vous vous retrouverez avec beaucoup de cachettes et, fondamentalement, vous n'avez pas vraiment besoin de faire tout cela. Cela fonctionne mais je pense que c'est un peu désordonné
Colin Hebert
3
Je suis d'accord avec Colin. Si le script fonctionne, il ne devrait pas être nécessaire de créer une cachette. Ce qui pourrait être bon à considérer, c'est de lancer stash, puis stash pop. Les cachettes sautées peuvent être récupérées si nécessaire, mais vous ne vous retrouverez pas avec beaucoup de cachettes autrement. Cela laisse également un fichier supplémentaire traîner
Casebash
Que diriez-vous de sauter des fichiers binaires? Lorsque j'essaie d'appliquer l'extrait ci-dessus, je reçois des erreurs indiquant que le correctif ne peut pas être appliqué sans la ligne d'index complète! Ce qui me bat, c'est que je n'ai même pas touché ces fichiers / binaires en premier lieu!
tver3305
1
Je pense qu'à la fin de la première commande "git rm foo.patch" devrait juste être "rm foo.patch". Sinon, merci très utile.
Jack Casey
33

Créez un fichier de correctif contenant uniquement les modifications réelles (à l'exclusion des lignes avec uniquement des modifications d'espaces blancs), puis nettoyez votre espace de travail et appliquez ce fichier de correctif:

git diff> sauvegarde
git diff -w> changements
git reset --hard
patch <changements

Passez en revue les différences restantes, puis addet commitcomme d'habitude.

L'équivalent pour Mercurial est de faire ceci:

hg diff> sauvegarde
hg diff -w> modifications
hg revert --all
hg import - no-commit changes

Steve Pitchers
la source
Qu'est-ce qu'une question «protégée»? et Me Too répond. Je ne pense pas que cela
puisse
4
@jww Le cœur de la question de l'affiche originale est "comment éviter de commettre uniquement des changements d'espace blanc au contrôle de code source". L'OP utilise Git, mais cela s'applique également à tous les systèmes de contrôle de source que j'ai jamais utilisés. Cette réponse montre la procédure correcte si quelqu'un utilise Mercurial. Je pourrais imaginer que quelqu'un d'autre pourrait également contribuer à une solution pour les personnes utilisant Sublesion, etc.
Steve Pitchers
1
@jww et @ pagid: J'ai modifié ma réponse pour adresser spécifiquement Git, en utilisant la même approche que ma solution pour Mercurial. À mon avis, StackOverflow est plus qu'un simple forum Q + A - il a également un rôle de référentiel de connaissances. Des personnes autres que l'affiche originale pourraient bénéficier des réponses données et leurs circonstances varient. C'est pourquoi je pense que les réponses véhiculant un principe général sont valables, plutôt que de cibler une seule situation spécifique.
Steve Pitchers
@Steve - "J'ai modifié ma réponse pour adresser spécifiquement Git ..." - pourquoi n'avez-vous pas posé une nouvelle question dans le contexte de mercurial, puis ajouté votre propre réponse à la nouvelle question ???
2015
8
C'est en fait la plus propre, la plus compréhensible et la plus incassable des approches que j'ai vues.
Kzqai
12

La réponse la plus votée ne fonctionne pas dans tous les cas, en raison des espaces dans le contexte du correctif selon les utilisateurs dans les commentaires.

J'ai révisé la commande comme suit:

$ git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero

Cela génère un patch sans contexte. Cela ne devrait pas être un problème car le patch est de courte durée.

Alias ​​correspondant, encore une révision de ce qui était déjà fourni par les autres utilisateurs:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
void.pointer
la source
Pour ignorer uniquement les modifications d'indentation, j'ai dû utiliser à la --ignore-space-changeplace de -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
Andy
Un mot d'avertissement pour ne pas utiliser cette belle astuce sans contexte avec --ignore-blank-linesautre, vous trouverez des morceaux de diff corrigés à des décalages incorrects si certains des changements de l'espace blanc que vous cherchez à ignorer sont des suppressions / ajouts de lignes vides.
elbeardmorez
12

Ajoutez ce qui suit à votre .gitconfig:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"

Merci à la réponse de @Colin Herbert pour l'inspiration.

Explication de la syntaxe

La finale #doit être citée afin qu'elle ne soit pas traitée comme un commentaire à l'intérieur du .gitconfig, mais à la place est passée à travers et est traitée comme un commentaire à l'intérieur du shell - elle est insérée entre la fin du git applyet les arguments fournis par l'utilisateur qui gitles placent automatiquement au fin de la ligne de commande. Ces arguments ne sont pas recherchés ici - nous ne voulons git applypas les consommer, d'où le caractère de commentaire précédent. Vous voudrez peut-être exécuter cette commande GIT_TRACE=1 git anwpour voir cela en action.

Les --signaux signifient la fin des arguments et permettent le cas où vous avez un fichier nommé -wou quelque chose qui ressemblerait à un commutateur git diff.

Les guillemets doubles échappés $@sont requis pour conserver tous les arguments cités fournis par l'utilisateur. Si le "personnage n'est pas échappé, il sera consommé par l' .gitconfiganalyseur et n'atteindra pas le shell.

Note: l' .gitconfiganalyse syntaxique d'alias ne reconnaît pas les guillemets simples comme quelque chose de spécial - ses caractères spéciaux ne sont ", \, \net ;( en dehors d'une "chaîne entre guillemets). C'est pourquoi un "doit toujours être échappé, même s'il semble qu'il se trouve à l'intérieur d'une chaîne entre guillemets simples (dont git est complètement agnostique).

C'est important, par exemple. si vous disposez d'un alias pratique pour exécuter une bashcommande à la racine de l'arborescence de travail. La formulation incorrecte est:

sh = !bash -c '"$@"' -

Alors que la bonne est:

sh = !bash -c '\"$@\"' -
Tom Hale
la source
Excellent. Cela m'a permis d'ajouter un fichier à la fois. Outre l'ajout d'un répertoire racine pour un argument, existe-t-il un moyen de faire fonctionner cela comme «git add -A»?
Chucky
7

Qu'en est-il des éléments suivants:

git add `git diff -w --ignore-submodules |grep "^[+][+][+]" |cut -c7-`

La commande à l'intérieur des guillemets arrière obtient les noms des fichiers dont les espaces ne sont pas modifiés.

karmakaze
la source
2
ou juste git add `git diff -w |grep '^+++' |cut -c7-`si les sous-modules ne sont pas utilisés
karmakaze
-1

Vous devez d'abord déterminer si l'espace blanc de fin est intentionnel. De nombreux projets, y compris le noyau Linux, Mozilla, Drupal et Kerberos (pour n'en nommer que quelques-uns sur la page Wikipédia sur le style) interdisent les espaces vides. De la documentation du noyau Linux:

Obtenez un éditeur décent et ne laissez pas d'espace à la fin des lignes.

Dans votre cas, le problème est l'inverse: les validations précédentes (et peut-être les actuelles) n'ont pas suivi cette directive.

Je parie que personne ne veut vraiment l'espace blanc arrière, et résoudre le problème pourrait être un changement bienvenu. D'autres utilisateurs peuvent également rencontrer le même problème que vous. Il est également probable que le ou les contributeurs qui ajoutent des espaces de fin ne savent pas qu'ils le font.

Plutôt que d'essayer de reconfigurer git pour ignorer le problème, ou de désactiver la fonctionnalité autrement souhaitable dans votre éditeur, je commencerais par un message sur la liste de diffusion du projet expliquant le problème. De nombreux éditeurs (et git lui-même) peuvent être configurés pour gérer les espaces de fin.

Kevin Vermeer
la source
16
Ce n'est pas intentionnel, mais je ne peux pas changer la façon dont plus de 100 personnes qui contribuent au projet pensent. Cela ne les dérange pas et n'acceptera pas les correctifs avec plus de 1000 modifications, ce qui ne concerne que les espaces en fin de chaîne. Ils connaissent le problème et ont décidé de l'ignorer. Cette discussion a déjà eu lieu dans la liste et a été clôturée. Dans ce cas, c'est moi qui dois m'adapter à eux.
Edu Felipe
19
Configurez ensuite votre éditeur de manière à ce qu'il ne coupe pas les espaces de fin lorsque vous travaillez sur le code de ce projet.
jamessan
-2

J'ai trouvé un crochet git pré-commit qui supprime les espaces de fin . Cependant, si vous ne parvenez pas à faire en sorte que d'autres personnes l'utilisent, cela pourrait ne pas être une solution valide.

  #!/bin/sh

  if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
     against=HEAD
  else
     # Initial commit: diff against an empty tree object
     against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  fi
  # Find files with trailing whitespace
  for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do
     # Fix them!
     sed -i 's/[[:space:]]*$//' "$FILE"
  done
  exit
cmcginty
la source
4
Cette question demande comment conserver les espaces de fin.
Douglas
@Douglas: on pourrait probablement utiliser cette réponse pour créer un commit sur une branche temporaire, valider le vrai patch là-bas et choisir le diff uniquement dans la branche de travail d'une manière ou d'une autre ...
Tobias Kienzler