V oblasti verziovacích systémov koluje jedno rozšírené pravidlo:

Neprepisuj históriu.

A treba uznať, že je to dosť solídne pravidlo, ktoré sa obhajuje na mnohých miestach, napríklad na FS#45425 alebo inde. Jednoducho treba predpokladať, že keď niečo raz zverejníš, malo by to tak aj zostať. Ale niekedy je upratanie poriadku nielen nutné, ale môže sa z dlhodobého hľadiska vyplatiť.

Prepisovanie histórie nebude takmer istotne možné pre verejné projekty, kde iní pribudajú commity na tie tvoje. Ale pre úplne nové repozitáre niekde v zabudnutých kútoch internetu sa ten skok viery môže oplatit. Pre prácu, ktorá ešte nebola pushnutá, je čistenie takmer vždy bezpečné a veľmi vítané, takže poznať efektívne nástroje na to je nevyhnutné. A ešte lepšie je poznať nástroj, ktorý prácu vykoná, pričom používateľa nevystaví riziku neobnoviteľného poškodenia vo forme znehodnotenej histórie.

Začíname #

Existuje ešte jedno pravidlo, ktoré je v tomto kontexte obzvlášť relevantné:

Vždy maj viacero záloh.

Toto pravidlo ide ešte hlbšie. Nič nie je 100% spoľahlivé. Pred pokračovaním si zálohuj svoju prácu. Niektorý softvér má osvedčenú históriu bojového testovania, čo zvyčajne znamená, že okrajové prípady sú vybrúsené do bodu, keď nie sú viditeľné, ale môžeš sa vsadiť, že Murphy ťa vždy dostihne. Bol si varovaný. Nástroj, na ktorý sa pozrieme, je newren/git-filter-repo.

Pozor: Nesprávne použitie nástroja môže viesť ku katastrofickým scenárom.

Nástroj sa odporúča používať len na čerstvých klonoch, aby bola práca obnoviteľná v prípade havárie. Za každú cenu sa vyhni používaniu parametra --force, aby si predišiel strate dát.

Ak si nie si istý, použij --dry-run alebo --analyze spolu so skutočným príkazom na kontrolu zmien pred ich vykonaním. Teraz sa pozrime na niektoré prípady použitia tohto nástroja.

Nahradenie citlivého reťazca vo všetkých súboroch #

Najčastejším prípadom použitia pre prepisovanie git histórie je pravdepodobne odstraňovanie citlivých informácií, ako sú heslá alebo prístupové tokeny, ktoré boli omylom commitnuté. Nestačí len nahradiť všetky výskyty v aktuálnom indexe, pretože informácia môže byť stále prítomná v starších commitoch. Robiť to manuálne cez interaktívny rebase je časovo náročné a náchylné na chyby. Namiesto toho možno použiť tento príkaz:

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')

Dôvod pre syntax <( ... ) označujúcu I/O presmerovanie je, že argument --replace-text pôvodne vyžaduje deskriptor súboru s toľkými pármi kľúč-hodnota, koľko je potrebných. S vyššie uvedenou syntaxou možno úplne vynechať vytváranie súboru. Užitočné, keď je potrebné len jedno nahradenie.

Zvyčajne sa pri použití nástroja filter-repo končí tu. Je tiež ťažko zapamätateľné kvôli použitým shellovým záludnostiam a neobvyklej syntaxi vyžadujúcej dlhý symbol dvojitej šípky ==>, takže pri ďalšej potrebe pravdepodobne skončíš tým, že to opäť vyhľadáváš. Ale dá sa toho urobiť oveľa viac, takže sa pozrime na niektoré menej zdokumentované funkcie, ktoré som našiel roztrúsené po internete.

Odstránenie jedného priečinka, so zachovaním histórie #

Scenár, kde repozitár obsahuje priečinok, ktorý je potrebné z neho odstrániť bez akejkoľvek stopy v histórii:

git-filter-repo --path path_to_the_folder/ --invert-paths

Repozitár teraz neobsahuje žiadnu stopu po sledovaných súboroch v ./path_to_the_folder/. Treba si uvedomiť, že všetky nesledované súbory sú zachované, zatiaľ čo sledované súbory sú úplne vymazané. Ak sú všetky súbory v priečinku sledované, prázdny priečinok bude tiež zmazaný.

Extrahovanie jedného priečinka, so zachovaním histórie #

Opak je ešte jednoduchší s jedným parametrom navyše. Keď chceš extrahovať históriu commitov jedného priečinka, pričom vynecháš každý iný súbor:

git-filter-repo --path path_to_the_folder/

Repozitár teraz obsahuje iba ./path_to_the_folder/ a všetky ostatné nesledované súbory.

Presunutie všetkého z podpriečinka o úroveň vyššie #

Toto sa veľmi dobre hodí spolu s vyššie uvedeným príkazom. Po extrakcii niekedy potrebuješ obsah extrahovaného priečinka urobiť koreňom repozitára, čím posunúť všetko o úroveň vyššie v ceste:

git-filter-repo --path-rename path_to_the_folder/:

Všimni si znak dvojbodky : na konci. Repozitár teraz neobsahuje ./path_to_the_folder/ a namiesto toho vidíš obsah tohto priečinka priamo.

Nahradenie e-mailovej adresy v commitoch #

Toto sa trochu líši od vyššie uvedených príkazov, ale niekedy sa stane, že commity obsahujú nesprávnu e-mailovú adresu. Dá sa to opraviť vytvorením súboru .mailmap v požadovanom repozitári s nasledujúcim obsahom:

<new@email> <curent@email>

Treba poznamenať, že lomené závorky < a > okolo oboch e-mailových adries sú povinné, inak nastane nasledujúca chyba:

Unparseable mailmap file: line #1 is bad: ...

Keď je správne naformátovaný súbor .mailmap na mieste, vydaj príkaz na prepis:

git-filter-repo --use-mailmap

Aj keď zmena e-mailovej adresy v commitoch vyzerá ako nevinná zmena, aj tá mení SHA hashe commitov, pretože sa vypočítavajú aj s e-mailovou adresou autora.

Nahradenie mena autora v commitoch #

Variáciou vyššie uvedeného je nahradenie mena autora. Osobne som to nepoužil, ale viem si predstaviť situáciu použitia prezývky pre commity, ktoré chceš zverejniť, alebo opačný scenár, keď si urobil commity pod pravou identitou, ale chceš sa prezentovať len pod prezývkou. Všetky kroky sú rovnaké, s úpravou súboru .mailmap:

Name Surname <current@email> <current@email>

A znova spusti nasledovné:

git-filter-repo --use-mailmap

Samozrejme môžeš aj skombinovať zmenu autora aj e-mailu v jednom kroku:

Name Surname <new@email> <current@email>

Pamätaj, že meno autora sa zmení len pre commity, ktoré zodpovedajú current@email, čo treba mať na pamäti!

Kontrola zmien #

Po vykonaní zmien je vždy rozumné overiť, či všetko prebehlo správne. Jeden spôsob je použiť git grafické rozhranie na kontrolu všetkých vetví:

gitk --all

Ak GUI nie je k dispozícii, tento príkaz môže poslúžiť ako základ:

git log --graph --all --format='%h %an <%ae>'

Podľa potreby upraviť.

Záver #

git-filter-repo je veľmi všestranný nástroj, ktorý dokáže vykonať mnoho akcií jedným riadkom. Je to oficiálne preferovaný spôsob prepisovania git histórie. Väčšinou ho použiješ na odstraňovanie citlivých informácií, ako sú heslá, ale väčšina ďalších akcií potrebných na vyčistenie repozitára je tiež možná, keď poznáš správnu syntax. Nezabudni si robiť zálohy, neprepisuj verejné repozitáre, pokiaľ to nie je absolútne nevyhnutné, a udržuj svoje repozitáre čisté. Nech sa darí!

Odkazy #