Сворачивание истории репозитория git

85

У нас есть проект git с довольно большой историей.

В частности, в начале проекта в проекте было довольно много двоичных файлов ресурсов, теперь они удалены, поскольку фактически являются внешними ресурсами.

Однако размер нашего репозитория составляет> 200 МБ (общий размер проверки в настоящее время составляет ~ 20 МБ) из-за того, что эти файлы были ранее зафиксированы.

Что мы хотели бы сделать, так это «свернуть» историю так, чтобы репозиторий выглядел как созданный из более поздней ревизии, чем была. Например

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Репозиторий создан
  2. Добавлен большой набор бинарных файлов
  3. Удален большой набор бинарных файлов
  4. Новый предполагаемый «запуск» репозитория

Таким образом, мы хотим потерять историю проекта до определенного момента. На данный момент существует только одна ветка, поэтому нет никаких сложностей с попыткой иметь дело с несколькими начальными точками и т. Д. Однако мы не хотим терять всю историю и запускать новый репозиторий с текущей версией.

Возможно ли это, или мы обречены навсегда иметь раздутый репозиторий?

Гарет
источник

Ответы:

89

Вы можете удалить двоичный раздувание и сохранить остальную часть своей истории. Git позволяет вам переупорядочивать и «раздавливать» предыдущие коммиты, поэтому вы можете комбинировать только те коммиты, которые добавляют и удаляют ваши большие двоичные файлы. Если бы все добавления были выполнены в одном коммите, а удаления - в другом, это будет намного проще, чем работать с каждым файлом.

$ git log --stat       # list all commits and commit messages 

Найдите здесь коммиты, которые добавляют и удаляют ваши двоичные файлы, и обратите внимание на их SHA1, скажем 2bcdefи 3cdef3.

Затем, чтобы отредактировать историю репо, используйте rebase -iкоманду с ее интерактивной опцией, начиная с родителя фиксации, в которую вы добавили свои двоичные файлы. Он запустит ваш $ EDITOR, и вы увидите список коммитов, начинающийся с 2bcdef:

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

Вставьте squash 3cdef3как вторую строку и удалите строку с надписью pick 3cdef3из списка. Теперь у вас есть список действий для интерактивного, rebaseкоторый объединит коммиты, добавляющие и удаляющие ваши двоичные файлы, в один коммит, разница между которыми - это просто любые другие изменения в этих коммитах. Затем он повторно применит все последующие коммиты по порядку, когда вы скажете ему завершить:

$ git rebase --continue

Это займет минуту или две.
Теперь у вас есть репо, в котором больше нет бинарных файлов. Но они все равно будут занимать место, потому что по умолчанию Git хранит изменения в течение 30 дней, прежде чем их можно будет собрать сборщиком мусора, чтобы вы могли передумать. Если вы хотите удалить их сейчас:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git prune
$ git gc                      

Теперь вы удалили раздувание, но сохранили остальную часть своей истории.

Павел
источник
7
Вам просто нужно помнить, что если другие уже извлекали из этого репозитория, переписывание истории запутает их извлечение. В руководстве по git-rebase объясняется, как восстановить эти другие репозитории. kernel.org/pub/software/scm/git/docs/git-rebase.html
Отто,
это отличный ответ для конкретной проблемы пользователя, но не для реального вопроса! Ответ Давитенио - отличный ответ на актуальный вопрос.
Сэм Уоткинс,
27

Вы можете использовать git filter-branchс графтами, чтобы сделать коммит номер 4 новым корневым коммитом вашей ветки. Просто создайте файл.git/info/grafts с одной строкой, содержащей SHA1 фиксации номер 4.

Если вы сейчас выполните a git logили gitkвы увидите, что эти команды будут отображать фиксацию номер 4 как корень вашей ветки. Но на самом деле в вашем репозитории ничего не изменится. Вы можете удалить .git/info/graftsи вывод git logили gitkбудет как раньше. Чтобы на самом деле сделать коммит номер 4, вам придется запустить новый root git filter-branchбез аргументов.

Давитенио
источник
Это намного лучше, чем перебазирование, поскольку у него нет проблем с сохранением коммитов слияния и не вызывает изменение временных меток. Проще и быстрее, чем все методы rebase.
mmrobins 06
Собственно, есть ли способ физически удалить все коммиты, которые больше не являются частью этой ветки? git gc --prune=0похоже, не убирает их.
Verhogen
1
@verhogen git gc --prune=nowфизически очищает все коммиты, на которые больше не ссылаются. Если это не работает для вас, возможно, у вас есть ветка удаленного отслеживания, которая по-прежнему ссылается на старый корень. Перечислить с помощью git branch -r, затем удалить удаленную ветку, например, с помощью, git branch -rd origin/masterа затем git gc --prune=nowснова запустить .
kayahr
20

Благодаря сообщению JesperE, которое я изучил, git-filter-branchэто может быть именно то, что вам нужно. Похоже, вы также можете сохранить свои предыдущие коммиты, за исключением того, что они будут изменены после удаления ваших больших файлов. На странице руководства git-filter-branch :

Предположим, вы хотите удалить файл (содержащий конфиденциальную информацию или нарушение авторских прав) из всех коммитов:

git filter-branch --tree-filter 'rm filename' ЗАГОЛОВОК

Обязательно прочтите эту страницу руководства ... очевидно, вы захотите сделать это на запасном клоне вашего репозитория, чтобы убедиться, что он работает должным образом.

Пэт Нотц
источник
2
Посмотрите ссылку на github ... есть несколько мощных опций с помощью команды git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm
5

Это git-fast-exportто, что вы ищете?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
JesperE
источник