Заменить все выполненные замены на что-то другое - PullRequest
0 голосов
/ 08 мая 2018

Переписывая устаревшее приложение, я провел массовую замену foo на bar, смешанную со многими ручными изменениями. Некоторые замены пришлось отменить вручную, и в исходном коде уже было много других bar.

Теперь я вижу, что каждое foo, замененное на bar, должно быть 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'

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Вы можете использовать режим --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 - это версия, с которой были сделаны неправильные замены, которые должны быть исправлены
  • 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/'

Известные ограничения / проблемы :

  • Работает для одного файла. Чтобы исправить все неправильные замены в коммите, диапазоне фиксации или локальных изменениях, необходимо идентифицировать список измененных файлов и вызвать этот скрипт в цикле для всех из них.

  • Если во время первоначального (неправильного) замены без учета регистра использовался режим, то для части 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"
0 голосов
/ 10 мая 2018

Чтобы заменить foo: grep -rl 'foo'. | xargs sed -i 's / foo / bar / g'

Чтобы заменить bar: grep -rl 'bar'. | xargs sed -i 's / bar / baz / g'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...