git-filter-branch для удаления строк, но там, где строки содержат $ '\ и другие символы

Я пытаюсь переписать историю, используя:

git filter-branch --tree-filter 'git ls-files -z "*.php" |xargs -0 perl -p -i -e "s#(PASSWORD1|PASSWORD2|PASSWORD3)#xXxXxXxXxXx#g"' -- --all

как описано в этом руководстве.

Однако строки пароля, которые у меня есть, содержат все виды символов, отличных от A-Z, например. $ ' и \ вместо того, чтобы быть красивыми простыми строками типа «PASSWORD1» в приведенном выше примере.

Может ли кто-нибудь объяснить, что мне нужно для экранирования? Я нигде не смог найти это, и я боролся с этим часами.


person fooquency    schedule 05.09.2013    source источник
comment
Это не ответ на вопрос в его нынешнем виде. Но если пароли никогда не меняются с момента их первого введения в репозиторий, безусловно, было бы проще анонимизировать их (с помощью скрипта или вручную), зафиксировать это, а затем перебазировать фиксацию, чтобы переписать историю.   -  person James Cranch    schedule 06.09.2013
comment
@fooquency Пожалуйста, попробуйте мой скрипт и скажите, какие ошибки вы можете увидеть.   -  person konsolebox    schedule 06.09.2013


Ответы (4)


попробуйте BFG вместо git filter-branch...

Вы можете использовать намного более удобный формат замены, если используете BFG вместо git-filter-branch. Создайте файл passwords.txt с одним паролем в строке следующим образом:

PASSWORD1==>xXxXx      # Replace literal string 'PASSWORD1' with 'xXxXx'
ezxcdf\fr$sdd%==>xXxXx # ...all text is matched as a *literal* string by default

Затем запустите BFG с помощью этой команды:

$ java -jar bfg.jar -fi '*.php' --replace-text passwords.txt  my-repo.git

Вся история вашего репозитория будет просканирована, и все .php файлов (размером менее 1 МБ) будут заменены: любая совпадающая строка (которой нет в вашем последнем коммите) будет заменена.

... не нужно убегать

Обратите внимание, что единственная часть синтаксического анализа, которую BFG делает здесь с файлом подстановки, — это разделение на строку «==>», которая, вероятно, нет в ваших паролях, и весь текст интерпретируется буквально по умолчанию. .

Если вы хотите быть еще более кратким, вы можете опустить '==>' и все, что идет после него, в каждой строке (т. е. просто иметь файл паролей), и BFG заменит каждый пароль на строка '***REMOVED***' по умолчанию.

Обычно BFG в сотни раз быстрее, чем запуск git-filter-branch на большом репозитории и параметры адаптированы для этих двух распространенных вариантов использования:

  • Удаление Безумно больших файлов
  • Удаление паролей, учетных данных и других личных данных

Полное раскрытие: я автор BFG Repo-Cleaner.

person Roberto Tyley    schedule 06.09.2013
comment
*.php должно быть в кавычках, кажется: '*.php' - person fooquency; 06.09.2013
comment
На четырехъядерном компьютере с 800 строками и репозиторием 500 МБ это кажется намного медленнее, чем git-filter-branch: для достижения 1% требуется 5 минут. Можно ли ожидать этого от этого тома? - person fooquency; 06.09.2013
comment
@fooquency - спасибо за этот тест скорости, мне очень интересно - буквально впервые слышу, что BFG медленнее, чем git-filter-branch! Может случиться так, что 800 строк — это довольно много для сопоставления — не могли бы вы замерить пару тестовых прогонов с идентичной настройкой, но только с одним паролем, а затем повторить еще несколько? Лучше каждый раз запускать новую копию репозитория, поэтому, вероятно, стоит взять zip-архив свежей копии вашего репозитория, а затем разархивировать новую копию для каждого запуска. Я попытаюсь воссоздать эксперимент с репозиторием аналогичного размера (например, с ядром Linux). - person Roberto Tyley; 06.09.2013
comment
Спасибо. Я собираюсь попробовать это на второй копии репозитория, так как метод filter-branch выглядит так, как будто это займет больше времени, чем я думал, так что это вполне может закончиться быстрее. - person fooquency; 07.09.2013
comment
Сейчас я запускаю параллельный экземпляр, и он работает примерно в 10 раз быстрее. Два вопроса: есть ли возможность изменить строку замены по умолчанию, чтобы избежать предварительной обработки файла строк для добавления ==›xXxXxXxX, а не *** REMOVED *** (чисто из эстетических соображений)? Также полезна проверка ..contains n грязных файлов при запуске. Но есть ли способ заставить его перечислить их все (или ограничить, скажем, 10, а только первые два) и, кроме того, указать конкретную совпадающую строку и номер строки? В одном файле много строк, поэтому трудно найти то, что было забыто. - person fooquency; 08.09.2013
comment
@fooquency рад слышать, что у вас все идет быстрее! Ответы: 1) В настоящее время нет возможности изменить значение по умолчанию для строки замены с «*** REMOVED ***», и, хотя это разумный запрос функции, я несколько предвзято отношусь к ограничению количества параметров командной строки в чтобы не перегружать новых пользователей - если пользователь действительно так заботится об эстетике своей заменяющей строки, для него не составит большого труда добавить дополнительную информацию в файл паролей, как это сделали вы. 2) Да, это имеет смысл - возможно, вывод полного отчета о различиях в отдельный файл. - person Roberto Tyley; 08.09.2013
comment
извините - просто говорю, что 2) была хорошей идеей, но пока нет способа заставить BFG сделать это - я поработаю над этим для предстоящего выпуска. - person Roberto Tyley; 08.09.2013
comment
Спасибо. Да, он завершился намного быстрее и сделал свою работу. Спасибо за работу над программой. - person fooquency; 09.09.2013
comment
Имея вариант, например. --showunclean был бы действительно полезен - мне очень трудно идентифицировать строки в некоторых файлах, а итерация (только две за раз) медленная. - person fooquency; 15.09.2013
comment
извините, что вам потребовалось так много времени @fooquency - начиная с версии 1.11.0, BFG теперь пишет полные отчеты о «грязных» файлах в ваших защищенных коммитах. Отчеты записываются в виде файлов CSV, и включаются номера строк в затронутых файлах. - person Roberto Tyley; 02.10.2013

Опираясь на блестящую помощь, предоставленную konsolebox, которая действительно помогла мне решить эту проблему, решение, которое я в конечном итоге использовал с точки зрения выполнения этого через оболочку было:

Определите строки в файле, strings.txt

string1
another$string
yet! @nother string
some more stuff to re\move

Создайте сценарий Perl perl-escape-strings.pl, который будет использоваться для экранирования строк, где xXxXxXxXxXx — это строка, на которую они все будут заменены.

#!/usr/bin/perl

use strict;
use warnings;

while (<>)
{
        chomp;
        my $passwd = quotemeta($_);
        print qq|s/$passwd/xXxXxXxXxXx/g;\n|;
}

exit 0;

Баш-скрипт:

# Pre-process the strings
./perl-escape-strings.pl strings.txt > strings-perl-escaped.txt

# Change directory to the repo
cd repo/

# Define the filter command
FILTER="git ls-files -z '*.html' '*.php' | xargs -0 perl -p -i ../strings-perl-escaped.txt"

# Run the filter
git filter-branch --tree-filter "$FILTER" -- --all

Однако, поскольку количество строк велико, а мой репозиторий большой и содержит много тысяч коммитов, метод filter-branch занимает много времени. Итак, я собираюсь попробовать BFG, упомянутый в другом ответе, также параллельно, чтобы посмотреть, завершится ли он быстрее.

person fooquency    schedule 07.09.2013

Использование скрипта-оболочки:

#!/bin/bash

readarray -t PASSWORDS < list_file

REPLACEMENT='xXxXxXxXxXx'
SEP=$'\xFF'

EXPR=${PASSWORDS[0]}
for (( I = 1; I < ${#PASSWORDS[@]}; ++I )); do
    EXPR+="|${PASSWORDS[I]}"
done
EXPR="s${SEP}(${EXPR})${SEP}$REPLACEMENT${SEP}g"
EXPR=${EXPR//'\'/'\\\\'}; EXPR=${EXPR//'$'/'\\\$'}
EXPR=${EXPR//'"'/'\"'};   EXPR=${EXPR//'`','\`'}
EXPR=${EXPR//'^','\\^'};  EXPR=${EXPR//'[','\\['}
EXPR=${EXPR//']','\\]'};  EXPR=${EXPR//'+','\\+'}
EXPR=${EXPR//'?','\\?'};  EXPR=${EXPR//'.','\\.'}
EXPR=${EXPR//'*','\\*'};  EXPR=${EXPR//'{','\\{'}
EXPR=${EXPR//'}','\\}'};  EXPR=${EXPR//'(','\\('}
EXPR=${EXPR//')','\\)'}

FILTER="git ls-files -z '*.php' | xargs -0 perl -p -i -e \"$EXPR\""

echo "Number of passwords: ${#PASSWORDS[@]}"    
echo "Passwords:" "${PASSWORDS[@]}"
echo "EXPR: $EXPR"
echo "FILTER: $FILTER"

git filter-branch --tree-filter "$FILTER" -- --all
person konsolebox    schedule 05.09.2013
comment
Спасибо; просто сейчас пытаюсь. Да, у многих из них есть». - person fooquency; 06.09.2013
comment
Я думаю, что половина совпадения регулярного выражения не совсем правильная, потому что нет | разделитель: $ echo $FILTER дает: git ls-files -z '*.txt' | xargs -0 perl -p -i -e 's?(foo bar)?xXxXxXxXxXx?g' - person fooquency; 06.09.2013
comment
Кстати, пароли изначально берутся из файла, по одному на строку, так что, предположительно, назначение $PASSWORDS можно выполнить просто с помощью PASSWORDS=(`cat "/path/to/file"`) - person fooquency; 06.09.2013
comment
@fooquency Я попробовал скрипт, просто эхо по команде git. У меня был такой вывод: Passwords: PASSWORD1 PASSWORD2 PASSWORD3 git filter-branch --tree-filter git ls-files -z '*.php' | xargs -0 perl -p -i -e "s�(PASSWORD1|PASSWORD2|PASSWORD3)�xXxXxXxXxXx�g" -- --all. Пароли хорошо разделены с помощью |. Это делает значение переменной IFS. - person konsolebox; 06.09.2013
comment
@fooquency Вы имеете в виду, что каждый пароль находится в файле построчно? На самом деле так лучше. Вы можете просто использовать readarray, чтобы получить их, и это не нужно заключать в кавычки. - person konsolebox; 06.09.2013
comment
Да, пароли действительно по одному на строку, без кавычек. - person fooquency; 06.09.2013
comment
Хм.. Трубку я точно не вижу. Если я делаю echo $EXPR после первой строки EXPR=, я вижу каждую строку, но с пробелом между ними. Я на машине с Ubuntu; возможно, здесь происходит что-то специфичное для Ubuntu? - person fooquency; 06.09.2013
comment
@fooquency Сценарий запущен через bash script.sh? - person konsolebox; 06.09.2013
comment
Я пробовал это, и я получаю то же самое: показаны пробелы. Файл паролей содержит несколько строк с пробелами, так как некоторые фразы необходимо очистить. Будет ли это представлять проблему? - person fooquency; 06.09.2013
comment
Да, но только если вы не используете readarray. С readarray в качестве пароля включается вся строка, даже начальные и конечные пробелы. Вместо этого попробуйте использовать readarray. Это также может дать разницу в выводе переменной. Если это все еще не работает, мы соединим их вручную с помощью цикла. - person konsolebox; 06.09.2013
comment
Да, я использую readarray в начале. (Примечание. Я предполагаю, что в конце пробелов вы не включаете саму новую строку.) Эхо $PASSWORDS line показывает очень длинную строку без разрывов строки, только пробел между ними. - person fooquency; 06.09.2013
comment
Кстати, если я сделаю в командной строке IFS='|', затем echo $IFS, результатом будет пустая строка. Так что точно не ставится. - person fooquency; 06.09.2013
comment
Я сделал обновление, которое не зависит от IFS. Пожалуйста, попробуйте еще раз. На самом деле у меня есть идея, что, возможно, текстовый файл находится не в окончаниях строки \n? - person konsolebox; 06.09.2013
comment
Возможно, в какой-то момент они были отредактированы на компьютере с Windows. Я не уверен. Я запустил на нем dos2unix, чтобы убедиться. - person fooquency; 06.09.2013
comment
Кстати, строка echo "Passwords:" "${PASSWORDS[@]}" разделяет пароли только пробелами между ними, так как она еще не отформатирована. Надеюсь, вы уже попробовали новый скрипт. Если это правда, что окончание строки файла - CRLF, вы можете просто использовать для него dos2unix. - person konsolebox; 06.09.2013
comment
Отладочное отображение строки $EXPR или строки $FILTER определенно не показывает символ вертикальной черты. Однако, если я поставлю точку останова сразу после цикла, добавляющего канал, он там. Труба теряется во время EXPR="s${SEP}(${PASSWORDS[*]})${SEP}$REPLACEMENT${SEP}g" кажется. Ах, на самом деле, по-видимому, в этой строке больше не должно быть (${PASSWORDS[*]})? - person fooquency; 06.09.2013
comment
О да, извините, я не должен был включать это. Я сделал обновление. - person konsolebox; 06.09.2013
comment
Правильно, явный признак прогресса: сейчас я вижу замену струн. Однако, если строка исходного кода содержит $ в середине, последующие символы остаются. Так, например, если пароль был abc$efg, после запуска скрипта источник в репозитории будет содержать xXx$efg. У вас есть идеи на этот счет? (Большое спасибо за вашу помощь — я многому научился, даже работая здесь за полночь!) - person fooquency; 06.09.2013
comment
Вероятно, нам все еще нужно цитировать $ в отношении его синтаксиса с командой Perl s. Попробуйте изменить EXPR=${EXPR//'$'/'\$'} на EXPR=${EXPR//'$'/'\\\$'}. - person konsolebox; 06.09.2013
comment
Кроме того, скрипт не запустится (т. е. вызов запускается, но не завершается, оставляя пустую строку на терминале, как будто ожидая ввода чего-то еще), если строка в файле паролей содержит \ или $ . Если я удалю эти строки, он завершится. Ах, сейчас попробую ваше последнее предложение, которое было перечеркнуто в посте. - person fooquency; 06.09.2013
comment
Возможно, вам следует сделать это и с \\: EXPR=${EXPR//'\'/'\\\\'}. Обратите внимание, что он всегда должен быть в первой из этих команд подстановки. - person konsolebox; 06.09.2013
comment
Я еще не внес это изменение. (Не могли бы вы отредактировать основную запись, чтобы я мог быть уверен, что редактирую не то, что нужно? Я только что заметил, что то, что выглядело загадочно, на самом деле не так плохо, как я думал — теперь я вижу, что это несколько отдельных команд; может быть лучше поместить их в отдельные строки, чтобы было ясно). Во всяком случае, я писал, чтобы сказать: я только что попробовал файл паролей на себе. Большая часть вещей теперь превращается в xXxXxXxXxXx, и это хорошая новость. Однако строки со следующими символами не удаляются: @[]()?/, что, честно говоря, выглядит довольно знакомо. - person fooquency; 06.09.2013
comment
Кажется, я вижу, что делают эти пункты сейчас. Раньше не сталкивался с таким синтаксисом замены в переменной. - person fooquency; 06.09.2013
comment
Извините за задержку с ответом. Я попытался хорошо проанализировать, какие цитаты нужно делать, и сопоставил их с помощью редактора. Я пытаюсь представить, как это в конце концов повлияет на синтаксис Perl. Я надеюсь, что это работает сейчас на этот раз. - person konsolebox; 06.09.2013
comment
Еще раз спасибо за вашу невероятную настойчивость. Я попробовал это, и это не совсем сработало - этот список специальных символов все еще появлялся. Однако я понял, что могу использовать \Q...\E в каждой строке. Теперь это работает почти для всего — только пароли с $ и @ не стираются, и скрипт запускается, только если я удаляю эти пароли с \ in. Так что кажется, что я почти у цели. - person fooquency; 06.09.2013
comment
У меня кончились идеи :) Здесь я тестирую Perl-команду, у меня работает: echo '[]{}().*|@$?\' | perl -p -e 's:\[\]\{\}\(\)\.\*\|\@\$\?\\:works:'. Так что теперь я в замешательстве, почему мой последний метод не сработал. На самом деле я тоже пробовал \Q\E, но я не уверен, как бы я действительно это реализовал, поскольку некоторые символы, такие как '$', не включаются для него. Но попробую позже :) - person konsolebox; 06.09.2013
comment
С некоторой помощью местного эксперта по Perl мы обнаружили, что решение состоит в том, чтобы сначала предварительно обработать строки с помощью небольшого Perl-скрипта, чтобы выполнить все экранирование, затем собрать их в строку, а затем запустить команду filter. - person fooquency; 07.09.2013
comment
@fooquency Хорошо, что вы уже решили эту проблему :) Похоже, полагаться только на bash было почти невозможно. - person konsolebox; 07.09.2013
comment
Я только что опубликовал полное решение, которое я использовал. Тем не менее, ваши указатели были чрезвычайно полезны и помогли мне понять этот материал намного яснее, так что еще раз спасибо. - person fooquency; 07.09.2013
comment
@fooquency Не беспокойтесь. Добро пожаловать :) Эта тема может быть полезна и мне когда-нибудь, кто знает. Добавил в избранное :) - person konsolebox; 07.09.2013

Стройте его изнутри. Скажи пароль

a$b'c\d

Шаблон регулярного выражения будет

a\$b'c\\d

Одной из возможностей для команды perl может быть

perl -i -pe's/a\$b'\''c\\d/.../g'

(Обратите внимание, как каждый ' был заменен на '\''.)

Теперь вам нужно включить это в одинарные кавычки, поэтому вы повторяете процесс.

... '... perl -i -pe'\''s/a\$b'\''\'\'''\''c\\d/.../g'\''' ...
person ikegami    schedule 06.09.2013