Как заменить текст из файлов в истории git? - PullRequest
30 голосов
/ 06 ноября 2010

Я всегда использовал git-клиент на основе интерфейса (smartGit) и поэтому не имел большого опыта работы с консолью git.

Однако теперь я сталкиваюсь с необходимостью замены строки во всех файлах .txt из истории (то есть не стирая весь файл, а просто подставляя строку). Я нашел следующую команду:

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

Я попробовал это, и, к сожалению, заметил, что, хотя пароль изменился, все двоичные файлы были повреждены. Изображения и т. Д. Будут повреждены.

Есть ли лучший способ сделать это, чтобы не повредить мои двоичные файлы?

Спасибо.

EDIT:

Я с чем-то перепутал. Фактический код, вызвавший повреждение двоичных файлов, был:

$ git filter-branch --tree-filter "find . -type f -exec sed -i -e 's/originalpassword/newpassword/g' {} \;"

Код вверху на самом деле удалил все файлы с моим паролем, как ни странно.

Ответы [ 4 ]

75 голосов
/ 31 марта 2013

Я бы рекомендовал использовать BFG Repo-Cleaner , более простую и быструю альтернативу git-filter-branch, специально предназначенную для перезаписи файлов из истории Git.

Вы должны внимательно выполнить следующие шаги: https://rtyley.github.io/bfg-repo-cleaner/#usage - но основной бит такой: скачайте банку BFG (требуется Java 7 или выше) и выполните эту команду:

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

Файл replacements.txt должен содержать все замены, которые вы хотите сделать, в таком формате (одна запись на строку - обратите внимание, что комментарии не должны быть включены):

PASSWORD1 # Replace literal string 'PASSWORD1' with '***REMOVED***' (default)
PASSWORD2==>examplePass         # replace with 'examplePass' instead
PASSWORD3==>                    # replace with the empty string
regex:password=\w+==>password=  # Replace, using a regex
regex:\r(\n)==>$1               # Replace Windows newlines with Unix newlines

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

Полное раскрытие: я являюсь автором репо-уборщика BFG.

34 голосов
/ 06 ноября 2010

Вы можете избежать касания нежелательных файлов, передав -name "pattern" в find.

Это работает для меня:

git filter-branch --tree-filter "find . -name '*.php' -exec sed -i -e \
    's/originalpassword/newpassword/g' {} \;"
6 голосов
/ 07 ноября 2011

Я создал файл в /usr/local/git/findsed.sh со следующим содержимым:

find . -name 'githubDirToSubmodule.sh' -exec sed -i '' -e 's/What I want to remove//g' {} \;

Я выполнил команду:

git filter-branch --tree-filter "sh /usr/local/git/findsed.sh"

Объяснение команд

Когда вы запускаете git filter-branch, он проходит каждую ревизию, которую вы когда-либо фиксировали, одну за другой.--tree-filter запускает сценарий findsed.sh для каждой зафиксированной ревизии, сохраняет его, а затем переходит к следующей ревизии.

Команда find находит определенный файл или набор файлов и выполняет (-exec)редактор sed в этом файле.sed - это команда, которая принимает регулярное выражение после s / и заменяет его строкой между / и / g (пустым в моем примере).{} - это ссылка на путь к файлу, который был задан командой find.Путь к файлу подается в sed, чтобы sed знал, над чем работать.\;просто завершает команду -exec.

Разделение сценария оболочки и команды на отдельные части позволяет уменьшить сложность, когда дело доходит до кавычек '' или "'.

Особенности

Я успешно реализовал это на Mac, и, очевидно, sed - это конкретная (более старая?) Версия для Mac.Это важно, так как иногда ведет себя по-разному.Убедитесь, что вы выполнили sed -i '', иначе он добавлял "-e" в конец файлов, думая, что это то, что я хотел назвать своими файлами резервных копий.-i '' говорит, что не делайте резервные копии, просто отредактируйте файлы на месте, и файл резервной копии не требуется.

Указание -name 'filename.sh' помогло мне избежать еще одной проблемы, которую я не мог решить.Был другой файл с .sh, и этот файл заканчивался без символа новой строки.По какой-то причине sed добавит в конец символ новой строки, несмотря на то, что s / blah / blah / g не соответствует никому в этом файле.Поэтому вместо того, чтобы выяснить эту проблему, я просто сказал находке игнорировать все остальные файлы.

Дополнительные команды, которые работают

Кроме того, я обнаружил, что эти команды работают вфайл findsed.sh (только одна команда за раз, не многократная, поэтому оставьте комментарий для остальных):

find . -name '.publishNewZenPackFromGithub.sh.swp' -exec rm -f {} \;
find . -name '*' -exec grep -H PassToRemove {} \;

Наслаждайтесь!

2 голосов
/ 06 ноября 2010

Может быть проблема расширения оболочки. Если ветвь фильтра теряет кавычки около "*.php" к тому времени, когда она оценивает команду, она может расширяться до нуля, таким образом git ls-files -z выводит список всех файлов.

Вы можете проверить источник ветки фильтра или попробовать разные приемы цитирования, но я бы просто сделал однострочный сценарий оболочки, который выполняет ваш древовидный фильтр, и вместо этого передал этот сценарий.

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