Згортання історії сховища 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
Отто

це чудова відповідь на конкретну проблему користувача, але не на власне питання! Відповідь davitenio - чудова відповідь на актуальне питання.
Сем Уоткінс

27

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

Якщо ви зараз зробите a, git logабо gitkпобачите, що ці команди відображатимуть номер коміту 4 як корінь вашої гілки. Але у вашому сховищі насправді нічого не зміниться. Ви можете видалити, .git/info/graftsі результат git logабо gitkбуде таким, як раніше. Щоб насправді зробити коміт номер 4 новим коренем, вам доведеться запустити git filter-branchбез аргументів.


Це набагато краще, ніж перебаза, оскільки у неї не виникає проблем із збереженням комітів злиття та не спричиняє зміни міток часу. Простіше і швидше, ніж усі методи перебазування.
mmrobins

Насправді, чи є спосіб фізично видалити всі коміти, які більше не є частиною цієї гілки? git gc --prune=0здається, не прибирає їх.
Верхоген

1
@verhogen git gc --prune=nowфізично очищає всі коміти, на які більше немає посилань. Якщо це не працює для вас, можливо, у вас є якась віддалена гілка відстеження, яка все ще посилається на старий root. Перелічіть за допомогою 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' HEAD

Не забудьте прочитати цю довідкову сторінку ... очевидно, ви хочете зробити це на резервному клоні вашого сховища, щоб переконатися, що вона працює належним чином.


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).
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.