Вы можете использовать режим --word-diff=porcelain
для git diff
(вместе с достаточно большим значением, передаваемым опции -U
, чтобы сохранить весь контекст между изменениями) и процессом вывод с достаточно простым скриптом, который исправит неправильную замену.
--word-diff[=<mode>]
Показать слово diff, используя <mode>
для разделения измененных слов. По умолчанию слова разделяются пробелами;
см. --word-diff-regex
ниже. <mode>
по умолчанию plain
, и
должен быть одним из:
- ...
porcelain
: используйте специальный формат на основе строк, предназначенный для использования скриптами. Добавленные / удаленные / неизмененные прогоны печатаются в обычном
унифицированный формат diff, начинающийся с символа + / - / `` на
начало строки и простирается до конца строки. Newlines
на входе представлены тильдой ~
на собственной строке.
Ниже вы найдете прототип sed
на основе реализации вышеуказанного подхода.
Использование
fix_wrong_replacements
path
revision
replacement_fix
, где
Эффекты
Предполагается, что рабочая копия файла на 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/'
Известные ограничения / проблемы :
Работает для одного файла. Чтобы исправить все неправильные замены в коммите, диапазоне фиксации или локальных изменениях, необходимо идентифицировать список измененных файлов и вызвать этот скрипт в цикле для всех из них.
Если во время первоначального (неправильного) замены без учета регистра использовался режим, то для части orig_pattern
аргумента replacement_fix
необходимо использовать [aA]
, [bB]
и т.д., регулярное выражение атома для каждой буквы.
Замены, непосредственно примыкающие к другим изменениям, не обрабатываются.
Иногда может быть добавлена лишняя пустая строка (из-за небольшого несоответствия в выводе git diff --word-diff
)
fix_wrong_replacements
#!/usr/bin/env bash
myname="$(basename "$0")"
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="$1"
revision="$2"
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"