Gitで行末が台無しになった-巨大な行末修正後に別のブランチからの変更を追跡する方法
質問
私たちは、定期的な更新を取得するサードパーティのPHPエンジンを使用しています。リリースはgitの別のブランチに保持され、フォークはマスターブランチです。
これにより、エンジンの新しいリリースからのパッチをフォークに適用できます。
私の問題は、ブランチに何度もコミットした後、エンジンの最初のインポートがCRLF行末で行われたことに気づいたことです。
すべてのファイルをLFに変換しましたが、これにより巨大なコミットが行われ、10万行が削除され、10万行が追加されました。 / p>
私は誰を知っていますか?どうすれば修正できますか?フォークにはすでに数百のコミットがあります。
良いことは、最初のインポートの後、独自のフォークを分岐する前に、何らかの形で行末修正コミットを行い、その巨大な行末コミットを履歴の後半で削除することです。
ただし、Gitでこれを行う方法はわかりません。
ありがとう!
解決
ようやく解決できました。
答えは:
git filter-branch --tree-filter '~/Scripts/fix-line-endings.sh' -- --all
fix-line-endings.shの内容:
#!/bin/sh
find . -type f -a \( -name '*.tpl' -o -name '*.php' -o -name '*.js' -o -name '*.css' -o -name '*.sh' -o -name '*.txt' -iname '*.html' \) | xargs fromdos
すべてのコミットのすべてのツリーですべての行末が修正された後、インタラクティブなリベースを行い、行末を修正していたすべてのコミットを削除しました。
レポジトリはクリーンでフレッシュになり、プッシュする準備ができました:)
訪問者への注意:レポジトリがプッシュ/クローンされた場合は、これがひどく混乱するため、これをしないでください!
他のヒント
今後、 git config --help
:
core.autocrlf
trueの場合、gitがファイルシステムから読み込むときにテキストファイルの行末の
CRLF
をLF
に変換し、ファイルシステムに書き込むときにgitを逆変換します。変数はinput
に設定できます。この場合、変換はファイルシステムからの読み取り中にのみ行われますが、ファイルは行末にLF
で書き出されます。ファイルは「テキスト」と見なされます。 ( ie はautocrlf
メカニズムの対象となります)ファイルのcrlf
属性に基づいて、またはcrlf
が指定されていない場合、ファイルの内容に基づきます。 gitattributes を参照してください。
git rebase
を見ましたか?
次のように、リポジトリの履歴をリベースする必要があります。
- ラインターミネータの修正をコミットする
- リベースを開始
- 最初にサードパーティのインポートコミットを残す
- ラインターミネータの修正を適用
- 他のパッチを適用する
理解する必要があるのは、これがすべてのダウンストリームリポジトリ(親リポジトリから複製されたもの)を破壊することです。理想的には、それらをゼロから始めることです。
更新:使用例:
target=`git rev-list --max-count=3 HEAD | tail -n1`
get rebase -i $target
最後の3つのコミットに対してリベースセッションを開始します。
今後、この問題を回避します:
1)全員が、末尾の空白を削除するエディターを使用し、すべてのファイルをLFで保存します。
2)1)が失敗した場合(何らかの理由で誰かが誤ってCRLFに保存した場合)、CRLF文字をチェックする事前コミットスクリプトがあります:
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by git-commit with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit" and set executable bit
# original by Junio C Hamano
# modified by Barnabas Debreceni to disallow CR characters in commits
if git rev-parse --verify HEAD 2>/dev/null
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
crlf=0
IFS="
"
for FILE in `git diff-index --cached $against`
do
fhash=`echo $FILE | cut -d' ' -f4`
fname=`echo $FILE | cut -f2`
if git show $fhash | grep -EUIlq
このスクリプトはGNU grepを使用し、Mac OS Xで動作しますが、他のプラットフォームで使用する前にテストする必要があります(CygwinとBSD grepで問題がありました)
3)ホワイトスペースエラーが見つかった場合、エラーのあるファイルに対して次のスクリプトを使用します。
#!/usr/bin/env php
<?php
// Remove various whitespace errors and convert to LF from CRLF line endings
// written by Barnabas Debreceni
// licensed under the terms of WFTPL (http://en.wikipedia.org/wiki/WTFPL)
// handle no args
if( $argc <2 ) die( "nothing to do" );
// blacklist
$bl = array( 'smarty' . DIRECTORY_SEPARATOR . 'templates_c' . DIRECTORY_SEPARATOR . '.*' );
// whitelist
$wl = array( '\.tpl', '\.php', '\.inc', '\.js', '\.css', '\.sh', '\.html', '\.txt', '\.htc', '\.afm',
'\.cfm', '\.cfc', '\.asp', '\.aspx', '\.ascx' ,'\.lasso', '\.py', '\.afp', '\.xml',
'\.htm', '\.sql', '\.as', '\.mxml', '\.ini', '\.yaml', '\.yml' );
// remove $argv[0]
array_shift( $argv );
// make file list
$files = getFileList( $argv );
// sort files
sort( $files );
// filter them for blacklist and whitelist entries
$filtered = preg_grep( '#(' . implode( '|', $wl ) . ')$#', $files );
$filtered = preg_grep( '#(' . implode( '|', $bl ) . ')$#', $filtered, PREG_GREP_INVERT );
// fix whitespace errors
fix_whitespace_errors( $filtered );
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// whitespace error fixer
function fix_whitespace_errors( $files ) {
foreach( $files as $file ) {
// read in file
$rawlines = file_get_contents( $file );
// remove \r
$lines = preg_replace( "/(\r\n)|(\n\r)/m", "\n", $rawlines );
$lines = preg_replace( "/\r/m", "\n", $lines );
// remove spaces from before tabs
$lines = preg_replace( "/\040+\t/m", "\t", $lines );
// remove spaces from line endings
$lines = preg_replace( "/[\040\t]+$/m", "", $lines );
// remove tabs from line endings
$lines = preg_replace( "/\t+$/m", "", $lines );
// remove EOF newlines
$lines = preg_replace( "/\n+$/", "", $lines );
// write file if changed and set old permissions
if( strlen( $lines ) != strlen( $rawlines )){
$perms = fileperms( $file );
// Uncomment to save original files
//rename( $file, $file.".old" );
file_put_contents( $file, $lines);
chmod( $file, $perms );
echo "${file}: FIXED\n";
} else {
echo "${file}: unchanged\n";
}
}
}
// get file list from argument array
function getFileList( $argv ) {
$files = array();
foreach( $argv as $arg ) {
// is a direcrtory
if( is_dir( $arg ) ) {
$files = array_merge( $files, getDirectoryTree( $arg ) );
}
// is a file
if( is_file( $arg ) ) {
$files[] = $arg;
}
}
return $files;
}
// recursively scan directory
function getDirectoryTree( $outerDir ){
$outerDir = preg_replace( ':' . DIRECTORY_SEPARATOR . '$:', '', $outerDir );
$dirs = array_diff( scandir( $outerDir ), array( ".", ".." ) );
$dir_array = array();
foreach( $dirs as $d ){
if( is_dir( $outerDir . DIRECTORY_SEPARATOR . $d ) ) {
$otherdir = getDirectoryTree( $outerDir . DIRECTORY_SEPARATOR . $d );
$dir_array = array_merge( $dir_array, $otherdir );
}
else $dir_array[] = $outerDir . DIRECTORY_SEPARATOR . $d;
}
return $dir_array;
}
?>
\r
このスクリプトはGNU grepを使用し、Mac OS Xで動作しますが、他のプラットフォームで使用する前にテストする必要があります(CygwinとBSD grepで問題がありました)
3)ホワイトスペースエラーが見つかった場合、エラーのあるファイルに対して次のスクリプトを使用します。
<*>
then
echo $fname contains CRLF characters
crlf=1
fi
done
if [ $crlf -eq 1 ]
then
echo Some files have CRLF line endings. Please fix it to be LF and try committing again.
exit 1
fi
exec git diff-index --check --cached $against --
このスクリプトはGNU grepを使用し、Mac OS Xで動作しますが、他のプラットフォームで使用する前にテストする必要があります(CygwinとBSD grepで問題がありました)
3)ホワイトスペースエラーが見つかった場合、エラーのあるファイルに対して次のスクリプトを使用します。
<*>1つのソリューション(必ずしも最適なソリューションではありません)は、 git-filter-を使用することですブランチを使用して、常に正しい行末を使用するように履歴を書き換えます。これは、少なくともより多くのコミットに対して、対話型のリベースよりも優れたソリューションになるはずです。また、git-filter-branchを使用してマージを処理する方が簡単かもしれません。
もちろん、履歴は公開されていないと仮定しています(リポジトリはクローンされていません)。