То, что вы хотите сделать, очень разрушительно, если вы опубликовали историю другим разработчикам. См. «Восстановление из исходной версии» в 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
что ты хочешь