Як видалити стару історію з сховища git?


208

Боюся, я не зміг знайти щось подібне до цього конкретного сценарію.

У мене є сховище git з великою кількістю історії: 500+ гілок, 500+ тегів, починаючи з середини 2007 року. Він містить ~ 19 500 комітів. Ми хотіли б видалити всю історію до 1 січня 2010 року, щоб зробити її меншою та легшою для роботи (ми б зберігали повну копію історії у сховищі архіву).

Я знаю, що я хочу зробити корінь нового сховища. Однак я не можу з’ясувати правильний git mojo, щоб урізати репо, щоб почати з цього фіксу. Я здогадуюсь про якийсь варіант

git filter-branch

залучення трансплантатів буде необхідним; вона також може бути необхідно для лікування кожного з 200+ гілок , які ми хочемо зберегти окремо , а потім патч репо разом (то , що я дійсно знаю , як це зробити).

Хто-небудь робив щось подібне? У мене git 1.7.2.3, якщо це має значення.

Відповіді:


118

Просто створіть прищепку батьківського нового кореневого зобов’язання перед жодним з батьків (або з порожнім кодом, наприклад, з реальним кореневим комітетом вашого сховища). Напрecho "<NEW-ROOT-SHA1>" > .git/info/grafts

Після створення трансплантата він набуває чинності відразу; ви повинні мати можливість подивитися git logі побачити, що непотрібні старі зобов’язання пішли:

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date:   Fri May 24 14:04:10 2013 +0200

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

Якщо все виглядає за призначенням, ви можете просто зробити простий, git filter-branch -- --allщоб зробити його постійним.

ПОПЕРЕДЖЕННЯ: після виконання кроку гілки фільтру всі ідентифікатори фіксації будуть змінені, тому кожен, хто використовує старе репо, ніколи не повинен зливатися з тим, хто використовує нове репо.


6
Довелося робити git filter-branch --tag-name-filter cat -- --allтеги, щоб оновити теги. Але у мене також є старі теги, які вказують на стару історію, яку я хочу видалити. Як я можу позбутися всіх цих старих тегів? Якщо я не видаляю їх, то старіші історії не зникають, і я все ще можу її бачити gitk --all.
Крейг МакКуїн

9
"Просто створити прищеплений елемент вашого нового кореневого зобов’язання для жодного з батьків" потрібна певна деталізація. Я спробував це і не зміг з'ясувати синтаксис "немає батьків". На сторінці вручну вказано, що потрібен ідентифікатор батьківської комісії використання всіх нулів просто дає мені помилку.
Маріус Гедмінас

6
Якщо хтось цікавився, як саме це працює, це досить просто:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle

3
Я згоден, пояснення, що таке трансплантат, було б більш ніж корисним
Чарльз Мартін

4
Цитується з пов'язаної сторінки вікі на трансплантатах. "Станом на Git 1.6.5 додана більш гнучка заміна git, яка дозволяє замінити будь-який об'єкт будь-яким іншим об'єктом і відстежувати асоціації за допомогою рефлексив, які можна натиснути і перетягнути між репост." Тому ця відповідь може бути застарілою для поточних версій git.
ThorSummoner

130

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

Якщо ви хочете звільнити простір у вашому git repo, але не хочете відновлювати всі ваші зобов’язання (перезавантаження або трансплантат), і все ж можете бути натисканням / потягуванням / злиттям від людей, які мають повне репо, ви можете використовувати git клон мілкий клон ( параметр --dethth ).

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10

; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin

Ви, можливо, зможете зменшити існуюче репо, виконавши наступні кроки:

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow

; Manually remove all other branches, tags and remotes that refers to old commits

; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now     ; Will actually delete your data

Як видалити всі локальні теги git?

Ps: Старіші версії git не підтримували клонування / push / тягнути з / до дрібної репозиції.


9
+1 Це правильна відповідь для нових версій Git. (О, і, будь ласка, поверніться до PPCG !)
wizzwizz4

6
Як ви можете cdдо папки, щойно видаленої? Я відчуваю, що тут є якась недостача інформація. Також, чи є можливість застосувати ці зміни до віддаленого репо?
Трогдор

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

4
Щоб відповісти на моє власне запитання: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Працює як шарм!
Мікрос

5
@Jez ти можеш перетворити свою дрібну репо в звичайну, запустивши git filter-branch -- --all. Це змінить усі хеши в ньому, але після цього ви зможете
перенести

61

Цей метод легко зрозуміти і прекрасно працює. Аргумент скрипту ( $1) - це посилання (тег, хеш, ...) на коміт, починаючи з якого ви хочете зберегти свою історію.

#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch

# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

ВКАЗАТИ, що старі теги залишатимуться присутніми; тож вам може знадобитися видалити їх вручну

зауваження: Я знаю, що це майже те саме, що і @yoyodin, але тут є кілька важливих додаткових команд та інформації. Я спробував відредагувати відповідь, але оскільки це суттєва зміна відповіді @ yoyodin, мою редакцію було відхилено, тож ось інформація!


Я ціную пояснення, надані для команд git pruneі git gc. Чи є пояснення для решти команд у сценарії? Наразі не ясно, які аргументи йому передаються і що робить кожна команда. Дякую.
користувач5359531

2
@ user5359531 дякую за ваше зауваження, я додав ще кілька коментарів до кожної команди. Сподіваюсь, це допомагає.
Кріс Мейз

4
Злиття конфліктів всюди ... не дуже корисно
Warpzit

3
@Warpzit Я позбувся конфліктів злиття, додавши -pдо rebaseкоманди, як це запропоновано в іншій відповіді
leonbloy

1
Я точно стежив за цим, і все, що я отримав, - це та сама історія, що і раніше, з новою гілкою, починаючи з комітету, до якого я хотів підрізатися, з усією тією ж історією, що і раніше. Історію не видалено
DrStrangepork

51

Спробуйте цей метод Як обрізати історію git :

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

Ось $1SHA-1 комітету, який ви хочете зберегти, і сценарій створить нову гілку, яка містить усі коміти між $1і, masterі вся старіша історія випадає. Зауважте, що цей простий скрипт передбачає, що у вас немає вже названої гілки temp. Також зауважте, що цей скрипт не очищує дані git для старої історії. Запустіть git gc --prune=all && git repack -a -f -F -dпісля того, як ви переконалися, що справді хочете втратити всю історію. Також вам може знадобитися, rebase --preserve-mergesале попередити, що реалізація цієї функції git не є ідеальною. Перевірте результати вручну, якщо ви їх використовуєте.


22
Я спробував це, але отримав конфлікт злиття за rebaseкрок. Дивно - я не очікував, що конфлікти злиття можуть бути можливими в цих умовах.
Крейг МакКуїн

2
Використовуйте, git commit --allow-empty -m "Truncate history"якщо комісія, яку ви перевірили, не містить жодних файлів.
friederbluemle

2
Як мені відсунути це назад до віддаленого майстра? Коли я це роблю, я закінчуюсь як старою, так і новою історією.
rustyx

1
Що таке "темп"? Що ви повинні надати як аргумент для цього? Чи є приклад того, як повинні виглядати ці команди, коли ви насправді виконуєте їх? Дякую.
користувач5359531

1
Я вважаю, що 1 долар - це хеш. (Більше деталей надано у прив'язаній статті).
Кріс Нолет

34

В якості альтернативи переписуванню історії розгляньте використання, git replaceяк у цій статті, з книги Pro Git . Обговорений приклад передбачає заміну батьківського комітету для імітації початку дерева, зберігаючи повну історію як окрему гілку для зберігання.


Так, я думаю, ви, напевно, могли б зробити те, що ми хотіли з цим, якби ви також заклали окрему гілку історії. (Ми намагалися зменшити сховище.)
ebneter

1
Мене не відштовхувало відповідь поза межами сайту; але воно посилається на сайт GitScm та підручник, на який він посилається, дуже добре написано і, здається, безпосередньо в питанні ОП.
ThorSummoner

@ThorSummoner Вибачте за це! Відповідь я розробимо трохи повніше на місці
Джефф Боуман

На жаль, це не є альтернативою перепису історії. На початку статті є заплутане речення, яке, мабуть, справило таке враження. Чи можна це відсторонити від цієї відповіді? У статті ви побачите, що автор переписує історію усіченої гілки, але пропонує спосіб повторного прив’язання до старої гілки «історія» за допомогою git replace. Я вважаю, що це було виправлено з іншого питання, де ви розмістили цю відповідь.
Мітч

1
Обговорення git replaceпроти git graftвиробляється на stackoverflow.com/q/6800692/873282
koppor

25

Якщо ви хочете зберегти в вгору по течії сховище з повною історією , але місцеві дрібні витягів, зробити неглибокий клон з git clone --depth=1 [repo].

Після натискання на фіксацію ви можете зробити

  1. git fetch --depth=1обрізати старі коміти. Це робить старі зобов’язання та їх об’єкти недоступними.
  2. git reflog expire --expire-unreachable=now --all. Термін дії всіх старих комітетів та їх об'єктів
  3. git gc --aggressive --prune=all для видалення старих предметів

Дивіться також Як видалити локальну історію git після вчинення комітету? .

Зауважте, що ви не можете натискати це "дрібне" сховище кудись ще: "дрібне оновлення не дозволено". Див. Розділ Віддалене відхилення (дрібне оновлення не дозволено) після зміни віддаленої URL-адреси Git . Якщо ви цього хочете, вам доведеться дотримуватися щеплення.


1
Точка № 1. змінила для мене різницю. Ура
клапас

21

Мені потрібно було прочитати кілька відповідей та іншу інформацію, щоб зрозуміти, що я роблю.

1. Ігноруйте все, що є старшим за певний вчинок

Файл .git/info/graftsможе визначити підроблені батьки для вчинення. Рядок із лише ідентифікатором комісії говорить, що у комітки немає батьківського. Якщо ми хотіли сказати, що нас цікавить лише останні 2000 комісій, ми можемо набрати:

git rev-parse HEAD~2000 > .git/info/grafts

git rev-parse дає нам ідентифікатор фіксації 2000-го батька поточного комітету. Наведена вище команда замінить файл трансплантатів, якщо він є. Перевірте, чи це там спочатку.

2. Перепишіть історію Git (необов’язково)

Якщо ви хочете зробити цей прищеплений підроблений батько справжнім, то запустіть:

git filter-branch -- --all

Це змінить усі ідентифікатори комісій. Кожна копія цього сховища повинна бути оновлена ​​оновленими.

3. Очистіть місце на диску

Я не зробив кроку 2, тому що хотів, щоб моя копія залишалася сумісною з верхнім потоком. Я просто хотів заощадити трохи дискового простору. Щоб забути всі старі зобов’язання:

git prune
git gc

Альтернатива: дрібні копії

Якщо у вас є неглибока копія іншого сховища і ви просто хочете зекономити трохи дискового простору, ви можете оновити .git/shallow. Але будьте обережні, щоб нічого не вказувало на поступку раніше. Отже, ви можете запустити щось подібне:

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc

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

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


Підтримка <GIT_DIR> / info / трансплантатів застаріла і буде видалена в майбутній версії Git.
Danny

Спробуйте скористатися git replaceнатомість. Див stackoverflow.com/questions/6800692 / ...
Joel AZEMAR

3

Під час перезавантаження або натискання на голову / майстер ця помилка може статися

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'

Щоб вирішити цю проблему, на приладовій панелі git слід видалити головну гілку із "Захищених гілок"

введіть тут опис зображення

тоді ви можете запустити цю команду

git push -f origin master

або

git rebase --onto temp $1 master

0

Тут є занадто багато відповідей, які не є актуальними, а деякі не повністю пояснюють наслідки. Ось що для мене підкорило історію за допомогою останнього git 2.26:

Спочатку створіть фіктивний коміт. Ця фіксація з’явиться як перша комісія у вашому усіченому репо. Вам це потрібно, тому що ця фіксація містить всі базові файли історії, яку ви зберігаєте. SHA - це ідентифікатор попереднього введення комітету, який ви хочете зберегти (у цьому прикладі 8365366). Рядок "Початковий" відображатиметься як повідомлення про фіксацію першого введення. Якщо ви використовуєте Windows, введіть команду нижче в командному рядку Git Bash.

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

Вище команда друку SHA, наприклад, d10f7503bc1ec9d367da15b540887730db862023.

Тепер просто введіть:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

Це спочатку покладе всі файли як-на-фіксацію 8365366до фіктивного комітету d10f750. Тоді воно відтворюватиме всі коміти після 8365366 за вершиною d10f750. Нарешті, masterвказівник гілки буде оновлений до останнього відтвореного комітету.

Тепер, якщо ви хочете натиснути ці усічені репо, просто зробіть git push -f.

Мало про що слід пам’ятати (це стосується інших методів, а також цього): Теги не передаються. Поки ідентифікатори фіксації та часові позначки зберігаються, ви побачите, як GitHub показує ці комісії в заголовку грудоподібної форми Commits on XY date.

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


-3

ви можете видалити каталог, файли, а також усю історію, пов’язану з dir або файлом, використовуючи вказаний нижче jar [завантажити його] та команди

Файл bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-folders folder_name git reflog закінчується --expire = now --all && git gc --prune = now --aggressive git push --mirror repo_url


-10
  1. видалити дані git, rm .git
  2. git init
  3. додати git remote
  4. силовий поштовх

6
це допоможе видалити ВСЮ історію, але не для того, що він просив: зберігати історію з січня 2010 року
Кріс Мейс

1
Просто хотів сказати спасибі, оскільки це допомогло мені в моєму сценарії, хоча це може бути не правильною відповіддю на питання
apnerve
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.