Как заменить строку во всей истории 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

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

Другие вопросы по тегам