Как удалить старую историю после запуска git filter-branch? - PullRequest
16 голосов
/ 13 мая 2011

Предположим, у меня есть такое дерево:

... -- a -- b -- c -- d -- ...
             \
              e -- a -- k

и я хочу, чтобы он стал просто

... -- a -- b -- c -- d -- ...

Я знаю, как прикрепить название ветви к "e". Я знаю, что то, что я собираюсь сделать, изменит историю, и это плохо. Также я думаю, что мне нужно использовать что-то вроде rebase или filter-branch. Но как именно - я потерялся.

Ok. Ситуация следующая: у меня сейчас довольно большое дерево (вот так)

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
           \               \
            t -- p -- l     y -- k

но в одном из моих первых коммитов (например, "b", например,) я добавил бинарные файлы, что делает весь репо очень тяжелым. Поэтому я решил забрать их. Я сделал это с помощью filter-branch. И теперь у меня есть 2 длинные ветви коммитов, идентичные друг другу, начиная со второго коммита.

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
      \    \               \
       \    t -- p -- l     y -- k
        \
         \             s'-- p'-- r'  
          \           /
           b'-- c'-- d'-- e'--- g'-- w'
                 \               \
                  t'-- p'-- l'    y'-- k'

где b '- коммит без двоичного файла. Так что я не могу сделать слияние. Я не хочу, чтобы все это дерево дублировалось в истории.

Ответы [ 5 ]

35 голосов
/ 13 мая 2011

После импорта репозитория Subversion с многолетней историей я столкнулся с похожей проблемой с раздуванием из-за большого количества бинарных ресурсов. В git: сокращение импорта Subversion , я описываю обрезку моего репозитория git с 4,5 ГиБ до примерно 100 МБ.

Предполагая, что вы хотите удалить из всех коммитов файлы, удаленные в «Удаление медиа-файлов» (6fe87d) , вы можете адаптировать подход из моего сообщения в блоге к своему репо:

$ git filter-branch -d /dev/shm/git --index-filter \
  "git rm --cached -f --ignore-unmatch media/Optika.1.3.?.*; \
   git rm --cached -f --ignore-unmatch media/lens.svg; \
   git rm --cached -f --ignore-unmatch media/lens_simulation.swf; \
   git rm --cached -f --ignore-unmatch media/v.html" \
  --tag-name-filter cat --prune-empty -- --all

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

Документация git filter-branch охватывает опцию --prune-empty.

--prune-empty
Некоторые виды фильтров генерируют пустые коммиты, которые оставляют дерево нетронутым. Этот переключатель позволяет git-filter-branch игнорировать такие коммиты…

Использование этой опции означает, что ваша переписанная история не будет содержать фиксацию «Удалить медиафайлы», поскольку она больше не влияет на дерево. Медиафайлы никогда не создаются в новой истории.

На этом этапе вы увидите дублирование в своем хранилище из-за другого задокументированного поведения .

Оригинальные ссылки, если они отличаются от переписанных, будут сохранены в пространстве имен refs/original/.

Если вас устраивает переписанная история, удалите резервные копии.

$ git for-each-ref --format="%(refname)" refs/original/ | \
  xargs -n 1 git update-ref -d

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

$ git reflog expire --verbose --expire=0 --all
$ git gc --prune=0

Теперь ваш локальный репозиторий готов, но вам нужно отправить обновления на GitHub. Вы можете сделать их по одному за раз. Для локальной ветки, скажем master, вы запустите

$ git push -f origin master

Скажем, у вас больше нет локальной ветки Issue5. У вашего клона все еще есть ссылка origin / issue5, которая отслеживает, где он находится в вашем хранилище GitHub. Запуск git filter-branch также изменяет все исходные ссылки, поэтому вы можете обновить GitHub без ветки.

$ git push -f origin origin/issue5:issue5

Если все ваши локальные ветви соответствуют их соответствующим коммитам на стороне GitHub (, т. Е. , нет невыполненных коммитов), тогда вы можете выполнить массовое обновление.

$ git for-each-ref --format="%(refname)" refs/remotes/origin/ | \
  grep -v 'HEAD$' | perl -pe 's,^refs/remotes/origin/,,' | \
  xargs -n 1 -I '{}' git push -f origin 'refs/remotes/origin/{}:{}'

На первом этапе выводится список имен:

$ git for-each-ref --format="%(refname)" refs/remotes/origin/
refs/remotes/origin/HEAD
refs/remotes/origin/issue2
refs/remotes/origin/issue3
refs/remotes/origin/issue5
refs/remotes/origin/master
refs/remotes/origin/section_merge
refs/remotes/origin/side-media-icons
refs/remotes/origin/side-pane-splitter
refs/remotes/origin/side-popup
refs/remotes/origin/v2

Нам не нужен псевдо-рефон HEAD и удалите его с помощью grep -v. В остальном мы используем Perl для удаления префикса refs/remotes/origin/ и для каждого запускаем команду вида

$ git push -f origin refs/remotes/origin/BRANCH:BRANCH
1 голос
/ 13 мая 2011

Вы можете использовать git filter-branch снова , но на этот раз с параметром --parent-filter.При этом вы можете отменить фиксацию, установив ссылки на их родителей ни к чему.Я думаю, что вы можете использовать опцию --commit-filter для той же цели.Это оставит много разных незакрепленных объектов в вашем репо, поэтому вам нужно выполнить git gc --prune = now.

Вот пример того, как --parent-filter можно использовать для удаленияродители http://git.661346.n2.nabble.com/purging-unwanted-history-td1507638.html

0 голосов
/ 13 мая 2011

Вы можете удалить ветви с помощью git branch -D branch_name и удалить удаленные ветви с помощью git push remote_name :branch_name.

Коммиты останутся без ссылки в вашем хранилище в течение некоторого времени (см. git gc doc ), но будут использовать дисковое пространство только в том случае, если позже вы поймете, что допустили ошибку.

И поскольку вы удалили удаленные ветви, новый git clone не должен извлекать несвязанные коммиты.

0 голосов
/ 13 мая 2011

Из вашего примера вы можете попробовать git rebase b b'?

0 голосов
/ 13 мая 2011

Попробуйте:

git branch -d name

Возможно, вам придется использовать это вместо:

git branch -D name

...