将所有已完成的替换更改为其他内容
Change all done replacements to something else
在重写遗留应用程序时,我将 foo
大量替换为 bar
,同时进行了许多手动更改。一些替换必须手动撤消,并且原始代码中已经有许多其他 bar
。
现在,我看到每个被 bar
替换的 foo
实际上应该是 baz
。
一个例子:
- 旧文件:
a staying "foo" and a replaced "foo" and a kept "bar"
- 新文件
a staying "foo" and a replaced "bar" and a kept "bar"
- 想要
a staying "foo" and a replaced "baz" and a kept "bar"
所需的操作很简单:将 foo
由 bar
替换为 baz
。我想知道是否有使用 git
或任何 Linux 工具的简单方法。
重新制定
也许这个单句表述更清楚:
给定一个文件的两个版本,将baz
放在旧版本包含foo
和新版本包含bar
的每个地方。
更多详情
实际上有三个完整的单词被不同长度的单词替换,比如
perl -pe 's/\babc\b/pqrs/gi; s/\bdefg\b/uvw/gi; s/\bhi\b/xyz/g'
替换 foo:grep -rl 'foo'。 | xargs sed -i 's/foo/bar/g'
替换栏:grep -rl 'bar'。 | xargs sed -i 's/bar/baz/g'
您可以使用 git diff
的 --word-diff=porcelain
模式(以及传递给 -U
选项的足够大的值,以保留更改之间的所有上下文)并使用足够简单的脚本处理其输出,该脚本将纠正错误的替换。
--word-diff[=<mode>]
Show a word diff, using the <mode>
to delimit changed words. By default, words are delimited by whitespace;
see --word-diff-regex
below. The <mode>
defaults to plain
, and
must be one of:
- ...
porcelain
: Use a special line-based format intended for script consumption. Added/removed/unchanged runs are printed in the usual
unified diff format, starting with a +/-/` ` character at the
beginning of the line and extending to the end of the line. Newlines
in the input are represented by a tilde ~
on a line of its own.
您将在下方找到上述方法的基于 sed
实现的原型。
用法:
fix_wrong_replacements
path
revision
replacement_fix
哪里
path
是文件在工作树中的(相对)路径
revision
是修订版,自此之后进行了必须修复的错误替换
replacement_fix
是
形式的字符串
/orig_pattern/incorrect_replacement_str/correct_replacement_str/
效果:
假设比较时 path
处文件的工作副本
其提交的修订 revision
包含替换结果
orig_pattern
与 incorrect_replacement_str
的某些实例,
识别这些替换并将它们更改为 correct_replacement_str
.
例子:
# In last two commits (and, maybe, in the working copy) some "int"s
# were incorrectly changed to "unsigned", now change those to "long"
$myname main.c HEAD~2 /int/unsigned/long/
# In the working copy of somefile.txt all "abc" case-insensitive words
# were changed to "pqrs", now change them to "xyz"
$myname somefile.txt HEAD '/[aA][bB][cC]/pqrs/xyz/'
已知limitations/issues:
它适用于单个文件。要修复提交、提交范围或本地更改中的所有错误替换,必须确定已更改文件的列表并在循环中为所有这些文件调用此脚本。
如果在原始(错误的)替换过程中使用了不区分大小写的模式,则 replacement_fix
[=103= 的 orig_pattern
部分] 参数必须为每个字母使用 [aA]
、[bB]
等正则表达式原子。
不处理紧邻其他更改的替换。
有时可能会添加多余的空行(因为git diff --word-diff
的输出略有不一致)
fix_wrong_replacements:
#!/usr/bin/env bash
myname="$(basename "[=11=]")"
if [ $# -ne 3 ]
then
cat<<END
Usage:
$myname <path> <revision> <replacement_fix>
where
- <path> is the (relative) path of the file in the working tree
- <revision> is the revision since which the wrong replacements that
must be fixed were made
- <replacement_fix> is a string of the form
/orig_pattern/incorrect_replacement_str/correct_replacement_str/
Effects:
Assuming that the working copy of the file at <path> when compared
to its committed revision <revision> contains results of replacing
certain instances of <orig_pattern> with <incorrect_replacement_str>,
identifies those replacements and changes them to <correct_replacement_str>.
Examples:
# In last two commits (and, maybe, in the working copy) some "int"s
# were incorrectly changed to "unsigned", now change those to "long"
$myname main.c HEAD~2 /int/unsigned/long/
# In the working copy of somefile.txt all "abc" case-insensitive words
# were changed to "pqrs", now change them to "xyz"
$myname somefile.txt HEAD '/[aA][bB][cC]/pqrs/xyz/'
END
exit 1
fi
file=""
revision=""
s=(${3//// })
orig_pattern="${s[0]}"
incorrect_replacement="${s[1]}"
correct_replacement="${s[2]}"
pat="-$orig_pattern\n+$incorrect_replacement"
git_word_diff()
{
git diff -U100000 \
--word-diff=porcelain \
--word-diff-regex='[[:alpha:]][[:alnum:]]*' \
"$@"
}
word_diff_file="$(mktemp)"
trap "rm $word_diff_file" EXIT
git_word_diff "$revision" -- "$file" > "$word_diff_file"
sed -n -e '
1,5 d;
/^-/ N;
/\n~$/ d;
/\n[- ]/ D;
/^'"$pat"'$/ {x;G;s/\n'"$pat"'$/'"$correct_replacement"'/;x;d;};
/^-.*\n+/ {s/^-.*\n+//;H;x;s/\n//;x;d;};
/^~$/ {s/.*//;x;p;d;};
{s/^.//;H;x;s/\n//;x;};
' "$word_diff_file" > "$file"
在重写遗留应用程序时,我将 foo
大量替换为 bar
,同时进行了许多手动更改。一些替换必须手动撤消,并且原始代码中已经有许多其他 bar
。
现在,我看到每个被 bar
替换的 foo
实际上应该是 baz
。
一个例子:
- 旧文件:
a staying "foo" and a replaced "foo" and a kept "bar"
- 新文件
a staying "foo" and a replaced "bar" and a kept "bar"
- 想要
a staying "foo" and a replaced "baz" and a kept "bar"
所需的操作很简单:将 foo
由 bar
替换为 baz
。我想知道是否有使用 git
或任何 Linux 工具的简单方法。
重新制定
也许这个单句表述更清楚:
给定一个文件的两个版本,将baz
放在旧版本包含foo
和新版本包含bar
的每个地方。
更多详情
实际上有三个完整的单词被不同长度的单词替换,比如
perl -pe 's/\babc\b/pqrs/gi; s/\bdefg\b/uvw/gi; s/\bhi\b/xyz/g'
替换 foo:grep -rl 'foo'。 | xargs sed -i 's/foo/bar/g'
替换栏:grep -rl 'bar'。 | xargs sed -i 's/bar/baz/g'
您可以使用 git diff
的 --word-diff=porcelain
模式(以及传递给 -U
选项的足够大的值,以保留更改之间的所有上下文)并使用足够简单的脚本处理其输出,该脚本将纠正错误的替换。
--word-diff[=<mode>]
Show a word diff, using the<mode>
to delimit changed words. By default, words are delimited by whitespace; see--word-diff-regex
below. The<mode>
defaults toplain
, and must be one of:
- ...
porcelain
: Use a special line-based format intended for script consumption. Added/removed/unchanged runs are printed in the usual unified diff format, starting with a +/-/` ` character at the beginning of the line and extending to the end of the line. Newlines in the input are represented by a tilde~
on a line of its own.
您将在下方找到上述方法的基于 sed
实现的原型。
用法:
fix_wrong_replacements
path
revision
replacement_fix
哪里
path
是文件在工作树中的(相对)路径revision
是修订版,自此之后进行了必须修复的错误替换
形式的字符串replacement_fix
是/orig_pattern/incorrect_replacement_str/correct_replacement_str/
效果:
假设比较时 path
处文件的工作副本
其提交的修订 revision
包含替换结果
orig_pattern
与 incorrect_replacement_str
的某些实例,
识别这些替换并将它们更改为 correct_replacement_str
.
例子:
# In last two commits (and, maybe, in the working copy) some "int"s
# were incorrectly changed to "unsigned", now change those to "long"
$myname main.c HEAD~2 /int/unsigned/long/
# In the working copy of somefile.txt all "abc" case-insensitive words
# were changed to "pqrs", now change them to "xyz"
$myname somefile.txt HEAD '/[aA][bB][cC]/pqrs/xyz/'
已知limitations/issues:
它适用于单个文件。要修复提交、提交范围或本地更改中的所有错误替换,必须确定已更改文件的列表并在循环中为所有这些文件调用此脚本。
如果在原始(错误的)替换过程中使用了不区分大小写的模式,则
replacement_fix
[=103= 的orig_pattern
部分] 参数必须为每个字母使用[aA]
、[bB]
等正则表达式原子。不处理紧邻其他更改的替换。
有时可能会添加多余的空行(因为
git diff --word-diff
的输出略有不一致)
fix_wrong_replacements:
#!/usr/bin/env bash
myname="$(basename "[=11=]")"
if [ $# -ne 3 ]
then
cat<<END
Usage:
$myname <path> <revision> <replacement_fix>
where
- <path> is the (relative) path of the file in the working tree
- <revision> is the revision since which the wrong replacements that
must be fixed were made
- <replacement_fix> is a string of the form
/orig_pattern/incorrect_replacement_str/correct_replacement_str/
Effects:
Assuming that the working copy of the file at <path> when compared
to its committed revision <revision> contains results of replacing
certain instances of <orig_pattern> with <incorrect_replacement_str>,
identifies those replacements and changes them to <correct_replacement_str>.
Examples:
# In last two commits (and, maybe, in the working copy) some "int"s
# were incorrectly changed to "unsigned", now change those to "long"
$myname main.c HEAD~2 /int/unsigned/long/
# In the working copy of somefile.txt all "abc" case-insensitive words
# were changed to "pqrs", now change them to "xyz"
$myname somefile.txt HEAD '/[aA][bB][cC]/pqrs/xyz/'
END
exit 1
fi
file=""
revision=""
s=(${3//// })
orig_pattern="${s[0]}"
incorrect_replacement="${s[1]}"
correct_replacement="${s[2]}"
pat="-$orig_pattern\n+$incorrect_replacement"
git_word_diff()
{
git diff -U100000 \
--word-diff=porcelain \
--word-diff-regex='[[:alpha:]][[:alnum:]]*' \
"$@"
}
word_diff_file="$(mktemp)"
trap "rm $word_diff_file" EXIT
git_word_diff "$revision" -- "$file" > "$word_diff_file"
sed -n -e '
1,5 d;
/^-/ N;
/\n~$/ d;
/\n[- ]/ D;
/^'"$pat"'$/ {x;G;s/\n'"$pat"'$/'"$correct_replacement"'/;x;d;};
/^-.*\n+/ {s/^-.*\n+//;H;x;s/\n//;x;d;};
/^~$/ {s/.*//;x;p;d;};
{s/^.//;H;x;s/\n//;x;};
' "$word_diff_file" > "$file"