web-dev-qa-db-ja.com

gitにコミットする前に末尾の空白を自動的に削除させます

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

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

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

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

210
mloughran

これらの設定(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スクリプト( this one など)にはnot「トレーリングを削除」機能はありませんが、「警告」機能は:

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

ただし、より優れたpre-commitフックを構築、特に次のことを考慮する場合は:

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


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

106
VonC

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

はい、これらはハッキングです。


堅牢なソリューション

次のGitエイリアスは、 my ~/.gitconfig から取得されます。

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

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

ツリーではなくインデックスを修正します

次のfixws Gitエイリアスは、インデックスに含まれるすべての空白エラーを修正しますが、ツリーに影響を与えません:

# 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-index Gitエイリアスは、インデックスとツリー(存在する場合)のすべての空白エラーを修正します。

# 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 -eを使用してIDエディターでパッチを「編集」する:

(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にpatchesの空白を修正するように指示します。

  2. 変更をpatchとして扱うようにGitを説得します:

    git add -up .
    

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

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

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

    git reset
    

GIT_EDITOR=:は、エディターとして:を使用することを意味し、コマンドとして:はIDです。

43
ntc2

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
29
cmcginty

Mac OS(またはおそらく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も忘れないでください。

または、( Gitコミットフック-グローバル設定 を介して)グローバルに使用する場合は、$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を再実行する主な理由は、新しく追加されたテンプレートを取得するためです。

19
AlexChaffee

このタスクはお気に入りのエディターに任せたいと思います。

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

10
Giacomo

mypre-commithooks を試してください。末尾の空白とを自動検出できますそれを削除。ありがとうございました!

GitBash(windows), Mac OS X and 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(-)
9
oldman

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

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

Git属性を使用し、git configでフィルターをセットアップする

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つのフェーズがあります。1つは、インデックスに追加またはチェックインするときに適用されるクリーンフェーズと、gitが作業ディレクトリにデータを入れるときのスマッジフェーズです。ここでは、ファイルの最後にない場合に末尾の改行文字を追加する可能性があることを除いて、catコマンドを使用してコンテンツを実行するだけです。 cleanコマンドは、 http://sed.sourceforge.net/sed1line.txt のメモからまとめた空白フィルタリングです。シェルスクリプトに入れる必要があるようです。ファイルの最後にある余分な余分な行をgit-configファイルに直接衛生化するなど、sedコマンドを注入する方法がわかりませんでした。 (末尾の空白を取り除くことができますが、別のsedスクリプトを必要とせずに、filter.fix-eol-eofを次のように設定するだけです。 sed 's/[ \t]*$//' %fここで、\tは実際のタブです(タブを押します)。

Require = trueを使用すると、問題が発生した場合にエラーが発生し、トラブルを回避できます。

Gitに関する私の言語が不正確な場合はご容赦ください。私は概念をかなりよく理解していると思うが、まだ用語を学んでいる。

8
zbeekman

以下は、ubuntu + mac os x互換バージョンです。

#!/bin/sh
#

# A git hook script to find and fix trailing whitespace
# 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

楽しんで

6
sdepold

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

egrep -rl ' $' --include *.Java *  | xargs sed -i 's/\s\+$//g'
5
Grant Murphy

Sublime Textユーザーの場合。

以下を適切に設定しますSetting-User設定。

"trim_trailing_white_space_on_save": true

2
Haris Krajina

ファイルのforループは、$ IFSシェル変数を使用します。指定されたスクリプトでは、$ IFS変数にも含まれる文字を含むファイル名は、forループ内の2つの異なるファイルとして表示されます。このスクリプトはそれを修正します:sed-manualのマルチラインモード修飾子は、ubuntuボックスではデフォルトで機能しないようです。そのため、別の実装を探しましたが、反復ラベルでこれを見つけました。正しく理解できた場合、ファイルの最終行。

#!/bin/sh
#

# A git hook script to find and fix trailing whitespace
# 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 seperator, introduces EOL-bug?
IFS='
'
# Find files with trailing whitespace
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( MacOSx-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 whitespace-characters
exec git diff-index --check --cached $against --

[1] sed-subsitionパターン: sedを使用して改行(\ n)を置き換える方法

2
imme

これは、コミットの前に空白を削除しません自動的にですが、それはかなり簡単です。次のPerlスクリプトを、$ PATHのdirにあるgit-wsf(git whitespace fix)という名前のファイルに配置しました。

git wsf | sh

gitがdiffとして報告するファイルの行からすべての空白onlyを削除します。

#! /bin/sh
git diff --check | Perl -x $0
exit

#! /usr/bin/Perl

use strict;

my %stuff;
while (<>) {
    if (/trailing whitespace./) {
        my ($file,$line) = split(/:/);
        Push @{$stuff{$file}},$line;
    }
}

while (my ($file, $line) = each %stuff) {
    printf "ex %s <<EOT\n", $file;
    for (@$line) {
        printf '%ds/ *$//'."\n", $_;
    }
    print "wq\nEOT\n";
}
1
davidc

ファイルの行末の末尾の空白を移植可能に削除するには、edを使用します。

test -s file &&
   printf '%s\n' H ',g/[[:space:]]*$/s///' 'wq' | ed -s file
0
nat

少し遅れますが、これは誰かを助けるかもしれないので、ここに行きます。

VIMでファイルを開きます。タブを空白に置き換えるには、vimコマンドラインに次のように入力します

:%s#\t#    #gc

他の末尾の空白を取り除くには

:%s#\s##gc

これはほとんど私のためにそれをしました。編集するファイルがたくさんあると面倒です。しかし、事前コミットフックや複数のエディターでの作業よりも簡単だと感じました。

0
hriddle