Как заменить строку во всей истории Git?
У меня один из моих паролей зафиксирован в нескольких файлах в моем репозитории Git. Есть ли способ автоматически заменить этот пароль какой-либо другой строкой во всей истории, чтобы его не было видно? В идеале, если бы я мог написать простой bash-скрипт, получающий строки для поиска и замены и выполняющий всю работу сам, что-то вроде:
./replaceStringInWholeGitHistory.sh "my_password" "xxxxxxxx"
Изменить: этот вопрос не является дубликатом этого вопроса, потому что я прошу о замене строк без удаления целых файлов.
2 ответа
Вначале я хотел бы поблагодарить ElpieKay, который опубликовал основные функции моих решений, которые я только автоматизировал.
Итак, наконец, у меня есть сценарий, который я хотел получить. Я разделил его на части, которые зависят друг от друга и могут служить независимыми сценариями. Это выглядит так:
censorStringsInWholeGitHistory.sh:
#!/bin/bash
#arguments are strings to censore
for string in "$@"
do
echo ""
echo "================ Censoring string "$string": ================"
~/replaceStringInWholeGitHistory.sh "$string" "********"
done
использование:
~/censorStringsInWholeGitHistory.sh "my_password1" "my_password2" "some_f_word"
replaceStringInWholeGitHistory.sh:
#!/bin/bash
# $1 - string to find
# $2 - string to replace with
for branch in $(git branch | cut -c 3-); do
echo ""
echo ">>> Replacing strings in branch $branch:"
echo ""
~/replaceStringInBranch.sh "$branch" "$1" "$2"
done
использование:
~/replaceStringInWholeGitHistory.sh "my_password" "********"
replaceStringInBranch.sh:
#!/bin/bash
# $1 - branch
# $2 - string to find
# $3 - string to replace with
git checkout $1
for file in $(~/findFilesContainingStringInBranch.sh "$2"); do
echo " Filtering file $file:"
~/changeStringsInFileInCurrentBranch.sh "$file" "$2" "$3"
done
использование:
~/replaceStringInBranch.sh master "my_password" "********"
findFilesContainingStringInBranch.sh:
#!/bin/bash
# $1 - string to find
# $2 - branch name or nothing (current branch in that case)
git log -S "$1" $2 --name-only --pretty=format: -- | sort -u
использование:
~/findFilesContainingStringInBranch.sh "my_password" master
changeStringsInFileInCurrentBranch.sh:
#!/bin/bash
# $1 - file name
# $2 - string to find
# $3 - string to replace
git filter-branch -f --tree-filter "if [ -f $1 ];then sed -i s/$2/$3/g $1;fi"
использование:
~/changeStringsInFileInCurrentBranch.sh "abc.txt" "my_password" "********"
Все мои скрипты находятся в моей домашней папке, что необходимо для правильной работы в этой версии. Я не уверен, что это лучший вариант, но пока я не могу найти лучший. Конечно, каждый скрипт должен быть исполняемым, чего мы можем достичь chmod +x ~/myscript.sh
,
Возможно, мой скрипт не оптимален, для больших репозиториев он будет обрабатываться очень долго, но он работает:)
И, в самом конце, мы можем перенести наше цензурированное репо на любой пульт с помощью:
git push <remote> -f --all
Редактировать: важный совет от ElpieKay:
Не забудьте удалить и воссоздать теги, которые вы нажали. Они по-прежнему указывают на старые коммиты, которые могут содержать ваш пароль.
Возможно, я улучшу свой сценарий в будущем, чтобы сделать это автоматически.
git filter-repo --replace-text
Git 2.25
man git-filter-branch
уже ясно рекомендует использовать
git filter-repo
вместо
git filter-tree
Итак, поехали.
Установите https://superuser.com/questions/1563034/how-do-you-install-git-filter-repo/1589985#1589985
python3 -m pip install --user git-filter-repo
а затем используйте:
echo 'my_password==>xxxxxxxx' > replace.txt
git filter-repo --replace-text replace.txt
или эквивалент с магией Bash:
git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')
Протестировано с помощью этого простого тестового репозитория: https://github.com/cirosantilli/test-git-filter-repository и заменяющих строк:
d1==>asdf
d2==>qwer
Вышеупомянутое действует на все ветки по умолчанию (так агрессивно!!!), чтобы действовать только на выбранные ветки, используйте: git filter-repo: можно ли его использовать в определенной ветке? например:
--refs HEAD
--refs refs/heads/master
Опция
--replace-text
вариант задокументирован по адресу: https://github.com/newren/git-filter-repo/blob/7b3e714b94a6e5b9f478cb981c7f560ef3f36506/Documentation/git-filter-repo.txt#L155
--replace-text <файл_выражений>::
Файл с выражениями, которые при обнаружении будут заменены. По умолчанию каждое выражение рассматривается как буквальный текст, но
regex:
иglob:
префиксы поддерживаются. Вы можете закончить строку==>
и некоторый текст замены, чтобы выбрать вариант замены, отличный от значения по умолчанию***REMOVED***
.
Конечно, после того, как вы опубликовали пароль публично, всегда будет слишком поздно, и вам придется изменить пароль, поэтому я даже не стал бы беспокоиться о замене в этом случае: удалите конфиденциальные файлы и их коммиты из истории Git
Кажется, это тот же вопрос: как заменить текст из файлов в истории git?
Проверено на git-filter-repo ac039ecc095d.
Сначала найдите все файлы, которые могут содержать пароль. Предположим, что пароль abc123
и ветвь master
, Возможно, вам придется исключить те файлы, которые имеют abc123
только как обычная строка.
git log -S "abc123" master --name-only --pretty=format: | sort -u
Затем замените "abc123" на "******". Предположим, один из файлов foo/bar.txt
,
git filter-branch --tree-filter "if [ -f foo/bar.txt ];then sed -i s/abc123/******/g foo/bar.txt;fi"
Наконец, принудительный толчок master
в удаленный репозиторий, если он существует.
git push origin -f master:master
Я сделал простой тест, и он сработал, но я не уверен, что все в порядке с вашим делом. Вам нужно разобраться со всеми файлами из всех веток. Что касается тегов, вам, возможно, придется удалить все старые и создать новые.