Как уменьшить размер раздутого репозитория Git, неинтерактивно сжимая все коммиты, кроме самых последних?

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

Как мне раздавить (или просто удалить) все коммиты, кроме самых последних, и сделать это без необходимости вручную раздавливать каждый в интерактивная перебазировка? В частности, я не хочу использовать

git rebase -i --root

Например, у меня есть эти коммиты:

A .. B .. C ... ... H .. I .. J .. K .. L

Я хочу этого (раздавить все между A и H в A):

A .. H .. I .. J .. K .. L

Или даже это будет работать нормально:

H .. I .. J .. K .. L

Существует ответ о том, как раздавить все коммиты, но я хочу сохранить некоторые из последних коммитов. Я также не хочу раздавливать самые последние коммиты . (В частности, мне нужно, чтобы первые два коммита считались сверху.)

(Редактировать, несколько лет спустя. Правильный ответ на этот вопрос — использовать правильный инструмент для работы. Git — не очень хороший инструмент для хранения резервных копий, каким бы удобным он ни был. Есть инструменты получше.)


person sanmai    schedule 11.06.2014    source источник
comment
Сотни ГБ в репозитории git? Это звучит как плохая идея...   -  person nneonneo    schedule 11.06.2014
comment
Можете ли вы привести пример того, что вы сделали бы своими руками?   -  person nneonneo    schedule 11.06.2014
comment
сквош и удаление - довольно разные операции; раздавливание сохраняет изменения, а удаление отменяет изменения (т.е. переустанавливает ваши последние изменения на какую-то более старую точку).   -  person M.M    schedule 11.06.2014
comment
@MattMcNabb верно, пусть будет kill вместо этого; что я имею в виду, что мне все равно, что с ними происходит, только мне нужны данные; например если мы сделаем снимок коммита 10004, удалим все коммиты до него и сделаем коммит 10004 корневым коммитом, я буду в порядке   -  person sanmai    schedule 11.06.2014
comment
@nneonneo обычная интерактивная перебазировка   -  person sanmai    schedule 11.06.2014
comment
@sanmai: Я имею в виду, во что бы вы отредактировали сценарий перебазирования?   -  person nneonneo    schedule 11.06.2014
comment
Наличие большого количества коммитов не обязательно приведет к увеличению размера вашего репозитория Git. Git очень эффективно сжимает текстовые файлы. Вы уверены, что количество коммитов является реальной проблемой, которая приводит к большому размеру вашего репо? Более вероятным кандидатом является то, что у вас слишком много версий двоичных ресурсов, которые Git не сжимает так же хорошо (или вообще не сжимает) по сравнению с обычными текстовыми файлами.   -  person    schedule 11.06.2014
comment
Должен быть лучший канонический вопрос, чем этот: Удалить старые двоичные версии из git и уменьшить размер репозитория git. К сожалению, это распространенная проблема, поэтому существует множество дубликатов.   -  person    schedule 11.06.2014
comment
@sanmai вам придется более четко определить, что вы подразумеваете под автоматическим. Вы имеете в виду, что хотите удалить коммиты с помощью одной команды? Однако это довольно тривиальная проблема, учитывая, что вы на самом деле не объяснили, каким образом размер вашего репо раздулся. Чем оно раздуто? Бинарные файлы? Как я уже говорил, Git довольно хорошо сжимает текстовые файлы. Отбрасывать совершенно хорошую историю из-за того, что вы версионировали двоичные файлы, не всегда может быть лучшим решением.   -  person    schedule 11.06.2014
comment
@Cupcake, конечно, если у вас их сотни гигабайт; git gc прошло несколько часов, прежде чем я начал копировать старые коммиты   -  person sanmai    schedule 11.06.2014
comment
@sanmai типичный (но, вероятно, не единственный способ) удаления двоичных файлов — использование git filter-branch с эффективной командой --index-filter. Если вы используете хороший индексный фильтр, операция должна выполняться довольно быстро.   -  person    schedule 11.06.2014
comment
@Cupcake Мне нужны мои файлы, иначе зачем мне вообще добавлять их в git?   -  person sanmai    schedule 11.06.2014
comment
@sanmai в целом, Git плохо подходит для управления версиями двоичных файлов из-за возможной проблемы с раздуванием размера. Я слышал, что некоторые люди используют git annex. Вы также можете изучить управление большими двоичными файлами с помощью git. Или вы можете просто продолжать уничтожать свою старую историю, когда ваше репо становится слишком большим. Вам решать.   -  person    schedule 11.06.2014
comment
@Cupcake Мне нужно сохранить первые два коммита, так что это даже близко не дубликат   -  person sanmai    schedule 23.07.2014
comment
@Cupcake Я добавил пример. Также мне нужно, чтобы это было сделано автоматически. Вопрос, о котором вы говорите, делает это вручную, где в вопросе я прямо говорю, что я не хочу делать это вручную.   -  person sanmai    schedule 23.07.2014
comment
@Cupcake, другой вопрос касается опции --root для git-rebase, поэтому этот вопрос не является одним и тем же вопросом, значит, это не дубликат.   -  person sanmai    schedule 23.07.2014
comment
@Cupcake спасибо за вашу помощь и щедрость. Я посмотрю, подходит ли ваш ответ достаточно скоро   -  person sanmai    schedule 23.07.2014
comment
Если кто-то ищет интерактивный способ раздавить первые X коммитов в своей истории коммитов, см. Combine первые две фиксации репозитория Git?.   -  person    schedule 23.07.2014


Ответы (3)


Оригинальный постер комментирует:

если мы сделаем снимок коммита 10004, удалим все коммиты до него и сделаем коммит 10004 корневым коммитом, я буду в порядке

Один из способов сделать это здесь, при условии, что ваша текущая работа называется branchname. Мне нравится использовать временный тег всякий раз, когда я выполняю большую перебазировку, чтобы дважды проверить отсутствие изменений и отметить точку, к которой я могу reset вернуться, если что-то пойдет не так (не уверен, что это стандартная процедура или нет, но она работает для меня):

git tag temp

git checkout 10004
git checkout --orphan new_root
git commit -m "set new root 10004"

git rebase --onto new_root 10004 branchname

git diff temp   # verification that it worked with no changes
git tag -d temp
git branch -D new_root

Чтобы избавиться от старой ветки, вам нужно удалить все теги и теги ветки на ней; тогда

git prune
git gc

очистит его от вашего репо.

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

person M.M    schedule 11.06.2014

Самое быстрое подсчет времени реализации почти наверняка будет с графтами и filter-branch, хотя вы могли бы добиться более быстрого выполнения с помощью commit-tree отработка последовательности rev-list вывод.

Rebase создан для применения изменений к другому содержимому. Здесь вы сохраняете содержимое и преднамеренно теряете историю изменений, которая его произвела, так что почти вся самая утомительная и медленная работа перебазирования тратится впустую.

Полезная нагрузка здесь, работающая с вашей фотографии,

echo `git rev-parse H; git rev-parse A` > .git/info/grafts  
git filter-branch -- --all

Документация для git rev-parse и git filter-branch.

Ветвь фильтра очень тщательно следит за тем, чтобы ее можно было восстановить после сбоя в любой момент, что, безусловно, является самым безопасным .... но это действительно полезно только тогда, когда восстановление путем простого повторения не будет быстрее и проще, если дела пойдут не так. Поскольку сбои редки, а перезапуск обычно дешев, нужно выполнить не «безопасную», но очень быструю операцию, которая почти наверняка сработает. Для этого лучший вариант здесь - сделать это на tmpfs (ближайшим известным мне эквивалентом в Windows будет виртуальный диск, например ImDisk), который будет молниеносно работать и не затронет ваш основной репозиторий, пока вы не будете уверены, что получили нужные результаты.

Итак, в Windows скажем, что T:\wip находится на виртуальном диске, и обратите внимание, что клон здесь ничего не копирует. А также читать документы на git clone's --shared, изучите внутренности клона, чтобы увидеть реальный эффект, это очень просто.

# switch to a lightweight wip clone on a tmpfs
git clone --shared --no-checkout . /t/wip/filterwork
cd !$

# graft out the unwanted commits
echo `git rev-parse $L; git rev-parse $A` >.git/info/grafts
git filter-branch -- --all

# check that the repo history looks right
git log --graph --decorate --oneline --all

# all done with the splicing, filter-branch has integrated it
rm .git/info/grafts

# push the rewritten histories back
git push origin --all --force

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

person jthill    schedule 23.07.2014
comment
Я убрал ссылки из вашего кода, потому что они выглядели так, будто они просто выделены синтаксисом, и не было очевидно, что это были ссылки. - person ; 23.07.2014

Проблема XY

Обратите внимание, что у оригинального автора есть проблема XY, когда он пытается выяснить, как раздавить свои старые коммиты (т. проблема Y), когда его реальная проблема на самом деле заключается в попытке уменьшить размер своего репозитория Git (проблема X), all-commits-except-for-the-most-recent-ones#comment37275724_24153548">как я уже упоминал в комментариях:

Наличие большого количества коммитов не обязательно приведет к увеличению размера вашего репозитория Git. Git очень эффективно сжимает текстовые файлы. Вы уверены, что количество коммитов является реальной проблемой, которая приводит к большому размеру вашего репо? Более вероятным кандидатом является то, что у вас слишком много версий двоичных ресурсов, которые Git не сжимает так же хорошо (или вообще не сжимает) по сравнению с обычными текстовыми файлами.

Несмотря на это, для полноты картины я также добавлю альтернативное решение к ответу Мэтта Макнабба к проблеме Y.

Удаление (сотни или тысячи) старых коммитов

Как уже отмечалось в оригинальном постере, использование интерактивной перебазировки с флагом --root может быть непрактичным, когда есть много коммитов (исчисляемых сотнями или тысячами), особенно потому, что интерактивная перебазировка не будет эффективно работать на таком большом количестве из них. .

Как указал Мэтт Макнабб в своем ответе, одним из решений является использование ветки-сироты в качестве нового (раздавленного) корня, а затем перебазирование поверх него. Другое решение — использовать пару различных сбросов ветки для достижения того же эффекта:

# Save the current state of the branch in a couple of other branches
git branch beforeReset
git branch verification

# Also mark where we want to start squashing commits
git branch oldBase <most_recent_commit_to_squash>

# Temporarily remove the most recent commits from the current branch,
# because we don't want to squash those:
git reset --hard oldBase

# Using a soft reset to the root commit will keep all of the changes
# staged in the index, so you just need to amend those changes to the
# root commit:
git reset --soft <root_commit>
git commit --amend

# Rebase onto the new amended root,
# starting from oldBase and going up to beforeReset
git rebase --onto master oldBase beforeReset

# Switch back to master and (fast-forward) merge it with beforeReset
git checkout master
git merge beforeReset

# Verify that master still contains the same state as before all of the resets
git diff verification

# Cleanup
git branch -D beforeReset oldBase verification

# As part of cleanup, since the original poster mentioned that
# he has a lot of commits that he wants to remove to reduce
# the size of his repo, garbage collect the old, dangling commits too
git gc --prune=all

Параметр --prune=all для git gc гарантирует, что все висячие коммиты удаляются сборщиком мусора, а не только те, которые старше 2 недель, что является настройкой по умолчанию для git gc.

person Community    schedule 11.06.2014