То, что вы хотите сделать, очень разрушительно, если вы опубликовали историю другим разработчикам. См. «Восстановление из исходной версии» в git rebaseдокументации, чтобы узнать о необходимых шагах после восстановления истории.
У вас есть как минимум два варианта: git filter-branchи интерактивная перебазировка, оба объяснены ниже.
С помощью git filter-branch
У меня была похожая проблема с объемными данными бинарных тестов из импорта Subversion и я писал об удалении данных из репозитория git .
Скажите, что ваша история с мерзавцами:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Обратите внимание, что git lolaэто нестандартный, но очень полезный псевдоним. С помощью --name-statusпереключателя мы можем видеть модификации дерева, связанные с каждым коммитом.
В коммите «Неосторожный» (имя объекта SHA1 - ce36c98) файл oops.isoпредставляет собой DVD-рип, случайно добавленный и удаленный при следующем коммите cb14efd. Используя технику, описанную в вышеупомянутом сообщении в блоге, команда для выполнения:
git filter-branch --prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
Опции:
--prune-emptyудаляет коммиты, которые становятся пустыми ( т.е. не меняют дерево) в результате операции фильтрации. В типичном случае эта опция производит более чистую историю.
-dназывает временный каталог, который еще не существует, чтобы использовать для построения отфильтрованной истории. Если вы работаете в современном дистрибутиве Linux, указание дерева /dev/shmприведет к более быстрому выполнению .
--index-filterявляется основным событием и работает с индексом на каждом шаге в истории. Вы хотите удалить, oops.isoгде бы он ни находился, но он присутствует не во всех коммитах. Команда git rm --cached -f --ignore-unmatch oops.isoудаляет DVD-рип, когда он присутствует, и не дает сбоя в противном случае.
--tag-name-filterописывает, как переписать имена тегов. Фильтр cat- это операция идентификации. Ваш репозиторий, как и в приведенном выше примере, может не содержать тегов, но я включил эту опцию для полной общности.
-- указывает конец опций git filter-branch
--allСледующее --является сокращением для всех ссылок. Ваш репозиторий, как и в приведенном выше примере, может иметь только один ref (master), но я включил эту опцию для полной общности.
После некоторого сбивания история теперь:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/ A oops.iso
| A other.html
|
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Обратите внимание, что добавляется только новый коммит «Неосторожный» other.htmlи что коммит «Remove DVD-rip» больше не находится в основной ветке. В ветке с пометкой refs/original/refs/heads/masterсодержатся ваши исходные коммиты на случай, если вы допустили ошибку. Чтобы удалить его, следуйте инструкциям в «Контрольном списке для сокращения хранилища».
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
Для более простой альтернативы клонируйте репозиторий, чтобы отбросить ненужные биты.
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
Использование file:///...клона URL копирует объекты, а не только создает жесткие ссылки.
Теперь ваша история:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Имена объектов SHA1 для первых двух коммитов («Индекс» и «Страница администратора») остались прежними, поскольку операция фильтрации не изменила эти коммиты. «Careless» потерял oops.isoи «Логин страница» получили новый родитель, так что их SHA1s сделал изменения.
Интерактивная перебазировка
С историей:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
Вы хотите удалить oops.isoиз «Неосторожного», как если бы вы никогда не добавляли его, а затем «Удалить DVD-рип» для вас бесполезно. Таким образом, наш план перехода к интерактивной перебазировке - сохранить «Страницу администратора», отредактировать «Неосторожный» и отказаться от «Удалить DVD-рип».
Запуск $ git rebase -i 5af4522запускает редактор со следующим содержимым.
pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
Выполняя наш план, мы изменяем его
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
То есть мы удаляем строку с «Удалить DVD-рип» и меняем операцию на «Неосторожно», editа не на pick.
При выходе из редактора при сохранении мы получаем командную строку со следующим сообщением.
Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Как говорится в сообщении, мы находимся в «небрежном» коммите, который хотим редактировать, поэтому мы запускаем две команды.
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
Первый удаляет поврежденный файл из индекса. Второй изменяет или изменяет «Careless», чтобы он был обновленным индексом, и -C HEADдает команду git повторно использовать старое сообщение коммита. Наконец, мы git rebase --continueпродолжаем с остальной частью операции rebase.
Это дает историю:
$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
что ты хочешь