Agregar solo cambios que no sean espacios en blanco

343

Tengo mi editor de texto para recortar automáticamente el espacio en blanco al guardar un archivo, y estoy contribuyendo a un proyecto de código abierto que tiene graves problemas con el espacio en blanco al final.

Cada vez que intento enviar un parche, primero debo ignorar todos los cambios de espacios en blanco a mano, para elegir solo la información relevante. No solo eso, sino que cuando corro git rebase, generalmente me encuentro con varios problemas debido a ellos.

Como tal, me gustaría poder agregar al índice solo cambios que no sean espacios en blanco, de manera similar git add -p, pero sin tener que elegir todos los cambios yo mismo.

¿Alguien sabe como hacer esto?

EDITAR: No puedo cambiar la forma en que funciona el proyecto, y han decidido, después de discutirlo en la lista de correo, ignorar esto.

Edu Felipe
fuente

Respuestas:

395

La solución @Frew no era exactamente lo que necesitaba, así que este es el alias que hice para el mismo problema:

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

O simplemente puede ejecutar:

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

Actualizar

Se agregaron opciones -U0y, --unidiff-zerorespectivamente, para solucionar problemas de coincidencia de contexto, según este comentario .

Básicamente, aplica el parche que se aplicaría addsin cambios de espacio en blanco. Notarás que después de git addnw your/fileque todavía habrá cambios sin clasificar, quedan los espacios en blanco.

El --no-color no es obligatorio, pero como tengo los colores establecidos para siempre, tengo que usarlo. De todos modos, más vale prevenir que curar.

Colin Hebert
fuente
77
Esto funcionó bien para mí, sin embargo, tuve que usarlo; de lo git apply --ignore-whitespacecontrario, el parche no se aplicaría por razones obvias.
jupp0r
106
Realmente debería haber una opción para agregar git, como git add -weso hizo esto.
Jarl
77
Esto me da problemas con patch does not applyy error while searching for... ¿Alguna idea?
DTI-Matt
18
No funcionó para mí. Tengo un patch does not applyerror
Jerry Saravia
13
Si usted está recibiendo 'parche falló debido a los espacios en blanco en el contexto como puntos @bronson fuera, este comando revisada obras (Genera un parche sin contexto): git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero. Esto no es arriesgado porque el índice ya está lo más actualizado posible, por lo que es una base confiable para el parche.
void.pointer
36

Esto funciona para mi:

Si quieres mantener un escondite, esto funciona

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

No me gustan los escondites, pero me he encontrado con un error en git + cygwin donde pierdo los cambios, así que para asegurarme de que las cosas fueron al registro, al menos configuré lo siguiente:

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

Básicamente creamos un diff que no incluye los cambios de espacio, revertimos todos nuestros cambios y luego aplicamos el diff.

Frew Schmidt
fuente
1
+1. Es posible que desee hacer un git stashpago en lugar de finalizar la compra, para tener una copia de seguridad de sus cambios, al menos hasta que se pruebe.
Paŭlo Ebermann
1
Terminarás con un montón de escondites y básicamente no necesitas hacer todo esto. Funciona pero creo que es un poco desordenado
Colin Hebert
3
Estoy de acuerdo con Colin. Si el script funciona, entonces no debería ser necesario crear un alijo. Sin embargo, lo que sería bueno considerar sería ejecutar stash, luego stash pop. Los escondites reventados se pueden recuperar si es necesario, pero de lo contrario no terminarás con muchos escondites. Esto también deja un archivo extra por ahí
Casebash
¿Qué hay de omitir archivos binarios? ¡Al intentar aplicar el fragmento anterior, recibo errores de que el parche no se puede aplicar sin la línea de índice completa! ¡Lo que me gana es que ni siquiera toqué estos archivos / binarios en primer lugar!
tver3305
1
Creo que al final del primer comando "git rm foo.patch" debería ser "rm foo.patch". De lo contrario, muy útil, gracias.
Jack Casey
33

Cree un archivo de parche que contenga solo los cambios reales (excluyendo líneas con solo cambios de espacio en blanco), luego limpie su espacio de trabajo y aplique ese archivo de parche:

git diff> backup
git diff -w> cambios
git reset --hard
patch <cambios

Revise las diferencias restantes, luego addy commitcomo de costumbre.

El equivalente para Mercurial es hacer esto:

hg diff> copia de seguridad
hg diff -w> cambios
hg revertir --todos los
importes de hg --no se comprometen los cambios

Steve Pitchers
fuente
¿Qué es una pregunta "protegida"? y Yo también respondo. No creo que esto incluso califique como una respuesta para mí porque parece que la pregunta se sacó de la nada ...
jww
44
@jww El núcleo de la pregunta del póster original es "cómo evitar cometer cambios de espacio en blanco solo en el control de fuente". El OP está usando Git, pero esto también se aplica a todos los sistemas de control de fuente que he usado. Esta respuesta muestra el procedimiento correcto si alguien usa Mercurial. Me imagino que otra persona también podría aportar una solución para las personas que usan Sublesion, etc.
Steve Pitchers
1
@jww y @ pagid: edité mi respuesta para abordar específicamente Git, usando el mismo enfoque que mi solución para Mercurial. En mi opinión, StackOverflow es más que otro foro de preguntas y respuestas : también tiene un rol como depósito de conocimiento. Las personas que no sean el póster original podrían beneficiarse de las respuestas dadas, y sus circunstancias varían. Es por eso que creo que las respuestas que transmiten un principio general son válidas, en lugar de enfocarse solo en una situación específica.
Steve Pitchers
@Steve - "Edité mi respuesta para abordar específicamente a Git ..." - ¿por qué no hiciste una nueva pregunta en el contexto de mercurial y luego agregaste tu propia respuesta a la nueva pregunta?
jww
8
Este es en realidad el más limpio, más comprensible y más inquebrantable de los enfoques que he visto.
Kzqai
12

La respuesta más votada no funciona en todos los casos, debido al espacio en blanco en el contexto del parche según los usuarios en los comentarios.

Revisé el comando de la siguiente manera:

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

Esto genera un parche sin contexto. No debería ser un problema ya que el parche es de corta duración.

Alias ​​correspondiente, nuevamente una revisión de lo que ya fue proporcionado por otros usuarios:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -
void.pointer
fuente
Para ignorar solo los cambios de sangría, tuve que usar en --ignore-space-changelugar de -w. git diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero
Andy
Una advertencia para que no use este encantador truco sin contexto, de lo --ignore-blank-linescontrario, encontrará parches de diferencias que se parcharán en compensaciones incorrectas si algunos de los cambios de 'espacio en blanco' que desea ignorar son eliminaciones / adiciones de líneas vacías.
elbeardmorez
12

Agregue lo siguiente a su .gitconfig:

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

Gracias a la respuesta de @Colin Herbert por la inspiración.

Explicación de sintaxis

La final #debe ser citada para que no se trate como un comentario dentro del .gitconfig, sino que se pase y se trate como un comentario dentro del shell: se inserta entre el final de los git applyargumentos proporcionados por el usuario y que gitse coloca automáticamente en el Fin de la línea de comando. Aquí no se quieren estos argumentos: no queremos git applyconsumirlos, de ahí el carácter de comentario anterior. Es posible que desee ejecutar este comando GIT_TRACE=1 git anwpara ver esto en acción.

Las --señales de final de argumentos y permite que para el caso de que usted tiene un archivo llamado -wo algo que se vería como un interruptor para git diff.

Las comillas dobles escapadas $@son necesarias para preservar cualquier argumento entregado por el usuario. Si el "personaje no se escapa, el .gitconfiganalizador lo consumirá y no alcanzará el shell.

Nota: .gitconfigel análisis de alias no reconoce las comillas sencillas como nada especial - sus caracteres especiales solamente son ", \, \n, y ;(fuera de una "cadena de -quoted). Esta es la razón por la "que siempre se debe escapar, incluso si parece que está dentro de una cadena entre comillas simples (de lo que git es completamente agnóstico).

Esto es importante, por ejemplo. si tiene un alias práctico para ejecutar un bashcomando en la raíz del árbol de trabajo. La formulación incorrecta es:

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

Mientras que el correcto es:

sh = !bash -c '\"$@\"' -
Tom Hale
fuente
Excelente. Esto me permitió agregar un archivo a la vez. Además de agregar un directorio raíz para un argumento, ¿hay alguna manera de hacer que esto funcione como 'git add -A'?
Chucky el
7

¿Qué tal lo siguiente:

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

El comando dentro de las comillas inversas obtiene los nombres de los archivos que tienen cambios que no son espacios en blanco.

karmakaze
fuente
2
o solo git add `git diff -w |grep '^+++' |cut -c7-`si no se utilizan submódulos
karmakaze
-1

Primero debe considerar si el espacio en blanco final es intencional. Muchos proyectos, incluidos el kernel de Linux, Mozilla, Drupal y Kerberos (por nombrar algunos de la página de Wikipedia sobre estilo) prohíben los espacios en blanco finales. De la documentación del kernel de Linux:

Obtenga un editor decente y no deje espacios en blanco al final de las líneas.

En su caso, el problema es al revés: las confirmaciones anteriores (y tal vez las actuales) no siguieron esta directriz.

Apostaría a que nadie realmente quiere el espacio en blanco final, y solucionar el problema podría ser un cambio bienvenido. Otros usuarios también pueden estar experimentando el mismo problema que usted. También es probable que los contribuyentes que agregan espacios en blanco finales no se den cuenta de que lo están haciendo.

En lugar de intentar reconfigurar git para ignorar el problema, o deshabilitar la funcionalidad deseable en su editor, comenzaría con una publicación en la lista de correo del proyecto explicando el problema. Muchos editores (y git en sí) pueden configurarse para ocuparse del espacio en blanco final.

Kevin Vermeer
fuente
16
No es intencional, pero no puedo cambiar la forma en que piensan más de 100 personas que contribuyen con el proyecto. No les importa, y no aceptarán parches con más de 1000 cambios que solo se ocupan de espacios en blanco finales. Saben sobre el problema y han decidido ignorarlo. Esta discusión ya sucedió en la lista y se cerró. En este caso, soy yo quien necesita adaptarse a ellos.
Edu Felipe
19
Luego configure su editor de modo que no recorte los espacios en blanco al trabajar en el código de este proyecto.
jamessan
-2

Encontré un gancho de confirmación previa de git que elimina los espacios en blanco finales . Sin embargo, si no puede lograr que otros usen esto, entonces podría no ser una solución válida.

  #!/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
fuente
44
Esta pregunta es cómo preservar los espacios en blanco finales.
Douglas
@ Douglas: probablemente se podría usar esta respuesta para crear una confirmación en una rama temporal, comprometer el parche real allí y seleccionar el diferencial solo en la rama de trabajo de alguna manera ...
Tobias Kienzler