Вступ: У вас є 5 рішень
В оригінальному плакаті зазначено:
Я випадково вчинив небажаний файл ... в моє сховище кілька комітетів тому ... Я хочу повністю видалити файл з історії сховища.
Чи можна переписати історію змін такою, яка filename.orig
ніколи не була додана до сховища?
Існує багато різних способів повністю видалити історію файлу з git:
- Внесення змін до комісій.
- Жорсткі перезавантаження (можливо плюс додаткова база).
- Неінтерактивна база даних.
- Інтерактивні знижки.
- Фільтрування гілок.
Що стосується оригінального плаката, внесення змін до комісії насправді не є варіантом само по собі, оскільки він зробив кілька додаткових зобов’язань згодом, але заради повноти я також поясню, як це зробити, тому що хто хоче змінити свої попередні зобов'язання.
Зауважте, що всі ці рішення включають в себе зміну / перезапис історії / зобов’язань одна за одною, тому кожному, хто має старі копії комітетів, доведеться зробити додаткову роботу, щоб повторно синхронізувати свою історію з новою історією.
Рішення 1: Змінення комітетів
Якщо ви випадково внесли зміни (наприклад, додавання файлу) у попередній фіксатор, і ви не хочете, щоб історія цієї зміни вже існувала, ви можете просто внести зміни до попереднього зобов’язання, щоб видалити файл із нього:
git rm <file>
git commit --amend --no-edit
Рішення 2: жорсткий перезавантаження (можливо плюс додаткова версія)
Як і рішення №1, якщо ви просто хочете позбутися попереднього зобов’язання, то у вас також є можливість просто зробити жорсткий перезавантаження для його батьків:
git reset --hard HEAD^
Ця команда буде важко скинути гілку до попереднього 1 - го батька фіксації.
Однак якщо ви, як і в оригінальному плакаті, зробили кілька комісій після виконання зобов’язання скасувати зміну, ви все одно можете використовувати жорсткі скидання, щоб змінити їх, але це також включає використання ребазу. Ось кроки, якими ви можете скористатися, щоб змінити зобов’язання ще в історії:
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
Рішення 3: Неінтерактивна база даних
Це спрацює, якщо ви просто хочете повністю видалити комісію з історії:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
Рішення 4: Інтерактивні знижки
Це рішення дозволить вам виконати ті самі речі, що і рішення №2 та №3, тобто змінити або видалити зобов’язання ще більше в історії, ніж ваше негайно попереднє зобов’язання, тож яке рішення ви будете використовувати, залежить від вас. Інтерактивні знижки не підходять для звільнення сотень комітетів з міркувань продуктивності, тому я б використовував неінтерактивні знижки або рішення гілки фільтра (див. Нижче) у таких ситуаціях.
Щоб розпочати інтерактивну базу даних, використовуйте наступне:
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
Це призведе до того, що git поверне історію фіксації назад на батьківський елемент комітету, який ви хочете змінити або видалити. Потім він представить вам список команд перемотування у зворотному порядку в будь-якому налаштованому git редактора (це за замовчуванням Vim):
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
Комісія, яку ви хочете змінити або видалити, буде вгорі цього списку. Щоб видалити його, просто видаліть його рядок у списку. В іншому випадку замініть "вибору" на "редагувати" на першому рядку так:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
Далі введіть git rebase --continue
. Якщо ви вирішили повністю видалити комітку, то це все, що вам потрібно зробити (крім перевірки, див. Остаточний крок цього рішення). Якщо, з іншого боку, ви хотіли змінити команду, git повторно застосує фільтр, а потім призупинить відновлення.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
У цей момент ви можете видалити файл і внести зміни до комісії, а потім продовжити ребазування:
git rm <file>
git commit --amend --no-edit
git rebase --continue
Це воно. На завершальному кроці, незалежно від того, змінили ви команду чи повністю її видалили, завжди гарно перевірити, чи не було внесено жодних інших несподіваних змін у вашу філію, порівнюючи її зі своїм станом перед ребатом:
git diff master@{1}
Рішення 5: Фільтрування гілок
Нарешті, це рішення найкраще, якщо ви хочете повністю стерти з історії всі сліди існування файлу, і жодне з інших рішень не відповідає цілі завдання.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
Це видалить <file>
усі коміти, починаючи з кореневої фіксації. Якщо замість цього ви просто хочете переписати діапазон фіксації HEAD~5..HEAD
, ви можете передати це як додатковий аргумент filter-branch
, як зазначено у
цій відповіді :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Знову ж таки, після filter-branch
завершення, зазвичай, корисно перевірити, чи немає інших несподіваних змін, порівнюючи вашу гілку з її попереднім станом перед операцією фільтрації:
git diff master@{1}
Альтернатива фільтру-філії: BFG Repo Cleaner
Я чув, що інструмент BFG Repo Cleaner працює швидше git filter-branch
, тому ви можете перевірити це як варіант. Це навіть офіційно згадується в документації фільтр-філії як життєздатна альтернатива:
git-filter-branch дозволяє робити складні переписані з оболонками ваші історії Git, але вам, мабуть, не потрібна ця гнучкість, якщо ви просто видаляєте небажані дані, наприклад, великі файли чи паролі. Для цих операцій ви можете розглянути BFG Repo-Cleaner , альтернативу Git-filter-гілки на основі JVM, як правило, принаймні на 10-50 разів швидше для цих випадків використання та з зовсім іншими характеристиками:
Будь-яка конкретна версія файлу очищається рівно один раз . BFG, на відміну від git-filter-branch, не дає вам можливості по-різному обробляти файл залежно від того, де або коли він був здійснений протягом вашої історії. Це обмеження дає основні переваги роботи BFG та добре підходить до завдання очищення поганих даних - вам не байдуже, де погані дані, ви просто хочете, щоб вони пройшли .
За замовчуванням BFG використовує в повній мірі багатоядерні машини, паралельно очищаючи файли дерев. ГИТ-фільтр-гілка Чистить фіксації послідовно (тобто в однопоточних чином), хоча це
можна писати фільтри , які включають в свої власні паралельності, в сценарії , що виконується на кожну фіксацію.
Ці опції команди набагато більш обмежувальні , ніж ГИТ-фільтр гілка, і присвячений тільки до завдань видалення небажаного даних-наприклад --strip-blobs-bigger-than 1M
.
Додаткові ресурси
- Pro Git § 6.4 Інструменти Git - Історія переписування .
- git-filter-branch (1) Сторінка посібника .
- git-commit (1) Сторінка посібника .
- git-reset (1) Сторінка посібника .
- git-rebase (1) Сторінка посібника .
- BFG Repo Cleaner (див. Також цю відповідь від самого творця ).