Poprawianie historii

Czasem się zdarzy, że postanowicie upublicznić prywatne repo, ale znajdujecie w nim plik konfiguracyjny, w którym na sztywno wpisaliście swoje hasło, którego używacie też gdzieś indziej (o tym, że jest to problemem przeczytasz w moim artykule o hasłach). Jak w miarę prosty sposób usunąć lub zmienić to hasło w historii?

Mówimy tu o repozytorium Git, najlepszego systemu kontroli wersji.

Zacznijmy od identyfikacji commita, który wprowadza nam zmiany, których chcemy się pozbyć. W moim przypadku była to aplikacja .NET, a hasło było zapisane w pliku app.config. Żeby zobaczyć ostatnie zmiany, ich autora i commit w danym pliku wpiszemy polecenie

$ git blame app.config

cb96ca9b (author [date]  1) <U+FEFF><?xml version="1.0" encoding="utf-8"?>
cb96ca9b (author [date]  2) <configuration>
...
3f40118a (author [date]  9) <connectionStrings>
3f40118a (author [date] 10)   <add name="oracleCnnStr"
3f40118a (author [date] 11)        connectionString="DataSource=#####;
3f40118a (author [date] 12)                          User Id=admin;
3f40118a (author [date] 13)                          Pwd=MyUnwantedPassword;" />
3f40118a (author [date] 14) </connectionStrings>
cb96ca9b (author [date] 15) </configuration>

Nieco skróciłem output polecenia git blame żeby zmieścił mi się na ekranie. Jak widzimy dodałem lub zmieniłem linie 9-14 w commicie o hashu zaczynającym się od 3f40118a. Teraz chcemy się dowiedzieć jak wyglądała ta zmiana

$ git log -p 3f40118a

diff --git a/app.config b/app.config
index 8b3f298..c513437 100644
--- a/app.config
+++ b/app.config
@@ -6,12 +6,10 @@
+<connectionStrings>
+       <add name="oracleCnnStr"
+        connectionString="DataSource=#####;
+                          User Id=admin;
+                          Pwd=MyUnwantedPassword;" />
+</connectionStrings>
</configuration>

Więc widzimy, że tylko dodaliśmy nasz connection string. Jeśli byłaby zmiana zawierająca wcześniej nasze hasło (np. zmieniliśmy DataSource) to musielibyśmy w kolejnym kroku cofnąć tę zmianę i wykonać cały proces od nowa tak aby wymazać hasło z pierwszego commita, w którym się pojawiło.

Żeby zmienić ten commit to wykonamy operację rebase. Chcemy zacząć ten proces na commicie o jeden wcześniej niż ten wskazany (~1). Więc napiszemy

$ git rebase -i 3f40118a~1

Co otworzy nam edytor tekstowy i będziemy mogli zmienić akcję przy pierwszym commicie na edit.

edit 3f40118 Add connection string
pick aa30723 Create Models and add initial business logic
pick a139e67 Add Parser
pick 3c77fff Fix parsing regex

Rebase jest bardzo potężnym narzędziem pozwalającym historię. Polecam się z nim dokładnie zapoznać: Dokumentacja, Rebasing.

Więc teraz jesteśmy cofnięci do momentu tuż po zacommitowaniu 3f40118a. Otwieramy nasz plik app.config, edytujemy go wedle woli, zapisujemy, dodajemy do indeksu za pomocą git add i wykonujemy

$ git commit --amend    # --no-edit (jeśli nie zmianiamy tytułu)
$ git rebase --continue

I wtedy pozostałe commity są aplikowane ponownie. Więc na koniec dostajemy to samo co na początku ze zmianą w tym jednym pliku w odpowiednim commicie.

Żeby teraz wypchnąć nasze repo, musimy użyć siły, ponieważ zmienilismy nasze commity (ich hashe).

$ git push -f

Właśnie tą metodą wyczyściłem credentiale w moim projekcie, aplikacji webowej korzystającej z bazy danych napisanej na przedmiot Bazy Danych, której kod znajdziecie na GitHubie.