Видалення файлу з історії
git rm не дає файлу з’являтися в нових комітах. Зі старими він не робить
нічого: блоб і далі в базі об’єктів, і далі в кожному клоні, і далі за один
git show від вас.
Це важливо у двох випадках — коли файл був секретом і коли він важив 400 МБ.
⌘K → Видалити файл з історії, або правий клік на файлі — у дереві проєкту,
у списку файлів коміту чи в редакторі повідомлення коміту. Саме коміт, який
видалив файл, зазвичай і є місцем, де хтось усвідомлює, що файл досі в
історії, тож вихід є і в тому меню теж.
Як знайти шлях
Два входи, бо вони відповідають на різні запитання.
Введіть його — відносно кореня репозиторію, без початкової скісної риски — коли ви вже знаєте, що прийшли видалити.
Перегляньте історію, коли не знаєте. Вона перелічує кожен шлях, який будь-коли комітили, найважчі згори, з кількістю версій кожного і позначкою, чи він досі відстежується. Видалені шляхи позначені окремо, і зазвичай саме вони вам і потрібні: файл, якого немає в робочому дереві, але який є в кожному клоні, — це рівно той випадок, якого звичайний діалог вибору файлу показати не може, бо файлу там просто немає.
Той самий список відповідає й на другу причину, з якою сюди приходять — чому цей клон важить два гігабайти — адже він відсортований за байтами, які реально займають блоби кожного шляху. Вибір рядка одразу дає вимірювання.

Виміряйте, перш ніж погоджуватися
Натисніть Виміряти (або виберіть рядок). Поки що нічого не записано. Ви отримуєте:
| Переписано комітів | Кожен коміт, починаючи з першого, який містив файл |
| Гілки / теги | Посилання, які зрушать з місця |
| Займають його блоби | Байти, які ці версії реально займають |
| Перший коміт | Звідки починається переписування — усе після нього отримає новий хеш |

Якщо лічильник показує нуль — шлях неправильний. Зазвичай це помилка в написанні або пропущений префікс каталогу, а не відсутність файлу.
Що насправді робить переписування
Gitcito копіює кожну гілку й тег у refs/gitcito/pre-purge/<timestamp>/…, а
потім запускає:
git filter-branch --force \
--index-filter 'git rm --cached --ignore-unmatch -- <path>' \
--prune-empty --tag-name-filter cat -- --branches --tags
--index-filter переписує індекс напряму, замість того щоб робити чекаут
кожного коміту, — це різниця між хвилинами й годинами. --branches --tags
замість --all тут навмисне: --all включив би й резервні посилання, і
переписування з’їло б власну страхувальну сітку.
Коміти, які не містили нічого, крім видаленого файлу, відкидаються
(--prune-empty). Теги перенаправляються на свої переписані коміти.
Резервна копія і чому місце ще не повертається
Чистку можна скасувати, і ціна цього в тому, що дисковий простір не звільняється, доки ви цього не скажете. Поки резервна копія існує, старі коміти все ще досяжні, тож git їх не збере.
| Дія | Наслідок |
|---|---|
| Відновити | Кожна гілка й тег повертаються до коміту, що був до чистки; файл повертається разом з ними |
| Видалити резервну копію | Видаляє резервні посилання, очищує рефлог, запускає git gc --prune=now — місце повернуто, чистка тепер остаточна |
Два кроки, а не один, бо перший — це та половина, яку можна відновити, а другий — ні.
Змініть секрет у будь-якому разі
Якщо секрет колись потрапив у пуш, переписування вашої історії не робить витік не-витоком. Хтось міг його зафетчити; сервери форджів тримають нереференсовані об’єкти; лог CI міг його надрукувати. Переписування зупиняє подальше поширення — воно не скасовує розкриття.
Змініть ключ. А тоді робіть чистку, щоб наступний, хто зробить клон, його там не знайшов.
Чого воно не робитиме
- Воно не робитиме пуш. Переписування — локальне. Опублікувати результат означає force push у кожну зачеплену гілку, і всім іншим доведеться переклонувати або зробити hard reset — рішення про це живе у захисті від force push.
- Воно відмовляється працювати з брудним робочим деревом чи посеред злиття або ребейзу. Переписування багато разів рухає HEAD, і робити це навколо незакомічених змін — найкоротший шлях їх втратити.
- Воно переписує за шляхом, а не за вмістом. Видалення секрету, який
вставили у файл із кодом, а не тримали в окремому файлі, потребує фільтра за
вмістом — це територія
git filter-repo --replace-text, і Gitcito її не обгортає. filter-branchповільний на дуже великих історіях. Але саме він іде в комплекті з git усюди, тому саме його Gitcito й використовує. На репозиторії з десятками тисяч комітівgit filter-repoу терміналі — швидший інструмент.- Чужі клони — це не ваш репозиторій. Вони зберігають стару історію, доки їх не переклонують.