234

チームで Git を使用しており、差分、ログ、マージなどから空白の変更を削除したいと考えています。これを行う最も簡単な方法は、Git が末尾の空白 (およびその他の空白) を自動的に削除することだと思います。適用されるすべてのコミットからの空白エラー)。

以下をファイルに追加しようとしました~/.gitconfigが、コミットしても何もしません。多分それは何か違うもののために設計されています。解決策は何ですか?

[core]
    whitespace = trailing-space,space-before-tab
[apply]
    whitespace = fix

誰かが Ruby 固有のアイデアを持っている場合に備えて、私は Ruby を使用しています。コミット前の自動コードフォーマットは次のステップですが、それは難しい問題であり、実際には大きな問題を引き起こしているわけではありません。

4

17 に答える 17

119

これらの設定 (core.whitespaceおよびapply.whitespace) は、末尾の空白を削除するためではなく、次のことを目的としています。

  • core.whitespace: それらを検出し、エラーを発生させます
  • apply.whitespace:それらを取り除きますが、「常に自動的に」ではなく、パッチ中にのみ

そのgit hook pre-commitためにはより良い仕事をすると思います(末尾の空白の削除を含む)


いつでもpre-commitフックを実行しないことを選択できることに注意してください。

  • 一時的に:git commit --no-verify .
  • 永続的に:cd .git/hooks/ ; chmod -x pre-commit

警告: デフォルトでは、pre-commitスクリプト (このようなもの) には「末尾の削除」機能はありませんが、次のような「警告」機能があります。

if (/\s$/) {
    bad_line("trailing whitespace", $_);
}

ただし、特に次のことを考慮すると、より良いフックを構築できます。pre-commit

ステージング領域にいくつかの変更を追加しただけで Git にコミットすると、作業コピーとして存在したことのない「アトミック」リビジョンが作成され、機能しない可能性があります。


たとえば、オールドマンは別の回答で、空白を検出して削除するフックpre-commit提案しています。
そのフックは各ファイルのファイル名を取得するため、特定の種類のファイルには注意することをお勧めします: (マークダウン) ファイルの末尾の空白を削除したくない.md!


コメントでハクレが提案した別のアプローチ:

\の前に" " を追加することで、マークダウンの行末に 2 つのスペースを含めることができます\n

次に、コンテンツ フィルター ドライバー:

git config --global filter.space-removal-at-eol.clean 'sed -e "s/ \+$//"'
# register in .gitattributes 
*.md filter=space-removal-at-eol
于 2009-02-26T19:19:01.453 に答える
49

Git をだまして変更をパッチとして処理させることで、Git をだまして空白を修正させることができます。「プレコミット フック」ソリューションとは対照的に、これらのソリューションは、空白を修正するコマンドを Git に追加します。

はい、これらはハックです。


堅牢なソリューション

次の Git エイリアスは、 私の~/.gitconfig.

「堅牢」とは、ツリーまたはインデックスがダーティであるかどうかに関係なく、これらのエイリアスがエラーなく実行され、正しいことを行うことを意味します。ただし、インタラクティブgit rebase -iが既に進行中の場合は機能しません。最後に説明されているトリックが機能するこのコーナーケースに関心がある場合は、追加のチェックについて~/.gitconfigを参照してください。git add -e

Git エイリアスを作成せずにシェルで直接実行したい場合は、二重引用符の間にすべてをコピーして貼り付けます (シェルが Bash のようなものであると仮定します)。

インデックスを修正しますが、ツリーは修正しません

次のfixwsGit エイリアスは、インデックス内のすべての空白エラーを修正しますが、ツリーには影響しません。

# Logic:
#
# The 'git stash save' fails if the tree is clean (instead of
# creating an empty stash :P). So, we only 'stash' and 'pop' if
# the tree is dirty.
#
# The 'git rebase --whitespace=fix HEAD~' throws away the commit
# if it's empty, and adding '--keep-empty' prevents the whitespace
# from being fixed. So, we first check that the index is dirty.
#
# Also:
# - '(! git diff-index --quiet --cached HEAD)' is true (zero) if
#   the index is dirty
# - '(! git diff-files --quiet .)' is true if the tree is dirty
#
# The 'rebase --whitespace=fix' trick is from here:
# https://stackoverflow.com/a/19156679/470844
fixws = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git stash save FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git stash pop && \
    git reset --soft HEAD~ ; \
  elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

アイデアは、インデックスに空白エラーがある場合git fixwsに実行することです。git commit

インデックスとツリーを修正する

次のfixws-global-tree-and-indexGit エイリアスは、インデックスとツリーのすべての空白エラーを修正します。

# The different cases are:
# - dirty tree and dirty index
# - dirty tree and clean index
# - clean tree and dirty index
#
# We have to consider separate cases because the 'git rebase
# --whitespace=fix' is not compatible with empty commits (adding
# '--keep-empty' makes Git not fix the whitespace :P).
fixws-global-tree-and-index = !"\
  if (! git diff-files --quiet .) && \
     (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~2 && \
    git reset HEAD~ && \
    git reset --soft HEAD~ ; \
  elif (! git diff-files --quiet .) ; then \
    git add -u :/ && \
    git commit -m FIXWS_SAVE_TREE && \
    git rebase --whitespace=fix HEAD~ && \
    git reset HEAD~ ; \
  elif (! git diff-index --quiet --cached HEAD) ; then \
    git commit -m FIXWS_SAVE_INDEX && \
    git rebase --whitespace=fix HEAD~ && \
    git reset --soft HEAD~ ; \
  fi"

バージョン管理されていないファイルの空白も修正するには、次のようにします。

git add --intent-to-add <unversioned files> && git fixws-global-tree-and-index

シンプルだが堅牢ではないソリューション

これらのバージョンはコピーと貼り付けが簡単ですが、副次的な条件が満たされていない場合、正しく動作しません。

現在のディレクトリをルートとするサブツリーを修正します (ただし、空でない場合はインデックスをリセットします)

git add -eID エディターでパッチを「編集」するために使用し:ます。

(export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset

インデックスを修正して保存します (ただし、ツリーがダーティであるか、インデックスが空の場合は失敗します)

git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset --soft HEAD~

ツリーとインデックスを修正します (ただし、空でない場合はインデックスをリセットします)

git add -u :/ && git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset HEAD~

export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .裏技の解説

この回答git rebase --whitespace=fixからトリックについて学ぶ前は、どこでもより複雑なトリックを使用していました。git add

手動で行った場合:

  1. 次のように設定apply.whitespacefixます (一度だけ実行する必要があります):

    git config apply.whitespace fix
    

    これは Git にパッチの空白を修正するように指示します。

  2. 変更をパッチとして扱うように Git を説得します。

    git add -up .
    

    +をa押しenterて、各ファイルのすべての変更を選択します。Git による空白エラーの修正に関する警告が表示されます。
    (git -c color.ui=auto diffこの時点で、インデックス化されていない変更が正確に空白エラーであることがわかります)。

  3. 作業コピーから空白エラーを削除します。

    git checkout .
    
  4. 変更を元に戻します (コミットする準備ができていない場合):

    git reset
    

エディターとして、またコマンドとして 使用GIT_EDITOR=:する手段がID です。::

于 2013-03-13T23:30:18.590 に答える
30

末尾の空白を削除するGit pre-commit フックを見つけました。

#!/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"
   git add "$FILE"
done
exit
于 2010-08-18T20:44:57.350 に答える
20

macOS (または、おそらくすべての BSD) では、sed コマンドのパラメーターは少し異なる必要があります。これを試して:

#!/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 -E 's/:[0-9]+:.*//' | uniq` ; do
    # Fix them!
    sed -i '' -E 's/[[:space:]]*$//' "$FILE"
    git add "$FILE"
done

このファイルを -- として保存する.git/hooks/pre-commitか、既に存在するファイルを探して、一番下のチャンクをその中のどこかに貼り付けます。そしてそれも覚えておいてchmod a+xください。

または、グローバルに使用する場合 ( Applying a git post-commit hook to all current and future repos を介して) に配置できます$GIT_PREFIX/git-core/templates/hooks(ここで、GIT_PREFIX は /usr または /usr/local または /usr/share または /opt/local/share です)。git init既存のリポジトリ内で実行します。

によるとgit help init

既存のリポジトリでの実行git initは安全です。すでに存在するものは上書きされません。再実行の主な理由git initは、新しく追加されたテンプレートを取得することです。

于 2011-01-04T18:31:48.513 に答える
13

この仕事はお気に入りの編集者に任せたいと思います。

保存時に末尾のスペースを削除するコマンドを設定するだけです。

于 2009-02-26T21:34:44.530 に答える
12

Git 属性の使用、および Git 構成によるフィルターのセットアップ

OK、これはこの問題を解決するための新しい方法です...私のアプローチは、フックを使用せず、フィルターと Git 属性を使用することです。これにより、開発する各マシンで、ファイルをコミットする前に、ファイルの末尾にある余分な末尾の空白と余分な空白行を取り除く一連のフィルターを設定できます。

次に、フィルターを適用する必要があるファイルの種類を示す .gitattributes ファイルを設定します。フィルターには 2 つのフェーズがcleanあり、ファイルをインデックスに追加するときに適用されるフェーズと、ファイルをsmudge作業ディレクトリに追加するときに適用されるフェーズがあります。

グローバル属性ファイルを探すように Git に指示する

まず、グローバル構成にグローバル属性ファイルを使用するように指示します。

git config --global core.attributesfile ~/.gitattributes_global

グローバル フィルターを作成する

次に、フィルターを作成します。

git config --global filter.fix-eol-eof.clean fixup-eol-eof %f
git config --global filter.fix-eol-eof.smudge cat
git config --global filter.fix-eol-eof.required true

sedスクリプト マジックを追加する

最後に、fixup-eol-eofスクリプトをパスのどこかに置き、実行可能にします。スクリプトは sed を使用して、オンザフライ編集を行います (行末のスペースと空白を削除し、ファイルの末尾にある不要な空白行を削除します)。

fixup-eol-eof は次のようになります。

#!/bin/bash
sed -e 's/[     ]*$//' -e :a -e '/^\n*$/{$d;N;ba' -e '}' $1

これの私の要点

新しく作成したフィルターを適用するファイルの種類を Git に伝える

最後に、お気に入りのテキスト エディターでファイル~/.gitattributes_globalを作成または開き、次のような行を追加します。

pattern attr1 [attr2 [attr3 […]]]

したがって、空白の問題を修正したい場合は、すべての C ソース ファイルに対して、次のような行を追加します。

*.c filter=fix-eol-eof

フィルターの考察

フィルターには 2 つのフェーズがあります。物事がインデックスに追加されたりチェックインされたりするときに適用されるクリーン フェーズと、Git が作業ディレクトリにデータを配置するときのスマッジ フェーズです。

catここでは、ファイルの末尾に改行文字がない場合に末尾の改行文字を追加する可能性があることを除いて、コマンドを使用して内容を実行しているだけです。

clean コマンドは、 http://sed.sourceforge.net/sed1line.txtのメモからまとめた空白フィルタリングです。シェルスクリプトに入れる必要があるようです。ファイルの最後にある不要な余分な行のサニテーションを git-config ファイルに直接挿入するなど、sed コマンドを挿入する方法がわかりませんでした。(ただし、別の sed スクリプトを必要とせずに、末尾の空白を取り除くことができます。を押して、 を実際のタブのfilter.fix-eol-eofようsed 's/[ \t]*$//' %fに設定するだけです。)\tTab

これrequire = trueにより、問題が発生した場合にエラーが発生し、トラブルを回避できます。

于 2015-02-11T04:07:35.450 に答える
9

私のpre-commit hooksを試してください。末尾の空白を自動検出して削除できます。

Git Bash (Windows)、Mac OS X、Linux で動作します!


スナップショット:

$ git commit -am "test"
auto remove trailing whitespace in foobar/main.m!
auto remove trailing whitespace in foobar/AppDelegate.m!
[master 80c11fe] test
1 file changed, 2 insertions(+), 2 deletions(-)
于 2014-03-28T04:44:37.460 に答える
9

以前の提案では、ターゲット ファイルの末尾に空白が多すぎる場合に読み取り不能なコミットが作成される傾向があるため、変更/追加した行から末尾の空白のみを削除するこの pre-commit フックを作成しました。

#!/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

IFS='
'

files=$(git diff-index --check --cached $against -- | sed '/^[+-]/d' | perl -pe 's/:[0-9]+:.*//' | uniq)
for file in $files ; do
    diff=$(git diff --cached $file)
    if test "$(git config diff.noprefix)" = "true"; then
        prefix=0
    else
        prefix=1
    fi
    echo "$diff" | patch -R -p$prefix
    diff=$(echo "$diff" | perl -pe 's/[ \t]+$// if m{^\+}')
    out=$(echo "$diff" | patch -p$prefix -f -s -t -o -)
    if [ $? -eq 0 ]; then
        echo "$diff" | patch -p$prefix -f -t -s
    fi
    git add $file
done
于 2013-10-14T07:33:56.440 に答える
7

Ubuntu および Mac OS X 互換バージョンは次のとおりです。

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# in your commits. Bypass it with the --no-verify option
# to git-commit
#

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]+:.*//' > /dev/null 2>&1 || sed -E 's/:[0-9]+:.*//') | uniq` ; do
  # Fix them!
  (sed -i 's/[[:space:]]*$//' "$FILE" > /dev/null 2>&1 || sed -i '' -E 's/[[:space:]]*$//' "$FILE")
  git add "$FILE"
done

# Now we can commit
exit
于 2011-06-07T08:44:15.787 に答える
5

今日はこれについて考えていました。これが私がJavaプロジェクトのためにやったことのすべてです:

egrep -rl ' $' --include *.java *  | xargs sed -i 's/\s\+$//g'
于 2012-09-20T07:54:14.863 に答える
2

ファイルのforループは$IFSシェル変数を使用します。指定されたスクリプトでは、$IFS 変数にもある文字を含むファイル名は、forループ内で 2 つの異なるファイルとして認識されます。

このスクリプトはそれを修正します: sedマニュアルで指定されている multiline-mode 修飾子は、私の Ubuntu ボックスではデフォルトでは機能しないようです。そのため、別の実装を探したところ、反復ラベルでこれが見つかりました。基本的には、私がそれを正しく理解していれば、ファイルの最後の行。

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# in your commits. Bypass it with the --no-verify option
# to git-commit
#

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

SAVEIFS="$IFS"
# only use new-line character as separator, introduces EOL-bug?
IFS='
'
# Find files with trailing white space
for FILE in $(
    git diff-index --check --cached $against -- \
    | sed '/^[+-]/d' \
    | ( sed -r 's/:[0-9]+:.*//' || sed -E 's/:[0-9]+:.*//' ) \
    | uniq \
)
do
# replace whitespace-characters with nothing
# if first execution of sed-command fails, try second one (Mac OS X version)
    (
        sed -i ':a;N;$!ba;s/\n\+$//' "$FILE" > /dev/null 2>&1 \
        || \
        sed -i '' -E ':a;N;$!ba;s/\n\+$//' "$FILE" \
    ) \
    && \
# (re-)add files that have been altered to Git commit-tree
#   when change was a [:space:]-character @EOL|EOF git-history becomes weird...
    git add "$FILE"
done
# restore $IFS
IFS="$SAVEIFS"

# Exit script with the exit-code of git's check for white space characters
exec git diff-index --check --cached $against --

1 sed-substitution pattern: sedを使用して改行 (\n) を置き換えるにはどうすればよいですか?

于 2011-10-16T23:44:40.453 に答える
-1

これはおそらく問題を直接解決するわけではありませんが、ファイル~/.gitconfig ではなくファイル./.git/configを編集する実際のプロジェクト空間でgit-configを介してそれらを設定することをお勧めします。すべてのプロジェクト メンバー間で設定の一貫性を保つことはよいことです。

git config core.whitespace "trailing-space,space-before-tab"
git config apply.whitespace "trailing-space,space-before-tab"
于 2009-02-26T19:12:18.333 に答える