Репозиторій та історія

Заміна та графт

git replace каже git: там, де ти збирався прочитати об’єкт A, читай натомість B. Нічого не переписується. Жоден sha не змінюється. Кожен коміт лишається рівно там, де був — git просто дивиться дорогою в інше місце.

Це звучить як курйоз, доки вам не знадобиться менший клон. Тоді це чесна альтернатива переписуванню історії: зробіть графт коміту на порожній список батьків — і все, що було до нього, зникає з логу, з графа і з кожного клону, зробленого звідти, — при цьому все ще зберігаючись, все ще будучи доступним для фетчу і за один видалений реф від повернення.

⌘K → Заміна та графт.

Наявні заміни, а під ними форма графта

Графт

Що ви даєте Що отримуєте
Коміт, без батьків Цей коміт стає початком історії
Коміт, один або більше батьків Він чіпляється туди, замість того місця, де реально сидить

Друга форма — цікавіша. Тримайте повну історію в архівному репозиторії, обріжте робочий, і графт, що вказує на верхівку архіву, знову з’єднає ці двоє — той самий трюк, яким GitHub віддає поверхневий клон, який усе одно можна поглибити.

Графт на порожній список батьків спершу перепитає, бо «історії немає» і «історія прихована» виглядають з логу однаково, а насправді це зовсім не те саме. Об’єкти живуть, доки їх не підчистить gc; див. обслуговування.

Життя з цим

Заміни — це рефи, під refs/replace/. З цього випливають три наслідки, які варто знати:

  • Вони локальні, доки їх не запушити: git push origin "refs/replace/*" ділиться ними, а той, хто клонує без них, бачить недоторкану історію.
  • Скасування працює — видалення рефа миттєво повертає справжню родинну лінію, і Gitcito записує графт як дію, яку можна скасувати, як і будь-яку іншу.
  • core.useReplaceRefs=false змушує git проігнорувати їх усі одним махом. Перемикач тут пише саме це, і діалог про це повідомляє, коли він вимкнений, бо репозиторій, який тихцем ігнорує власні заміни, — заплутане місце.

З командного рядка git --no-replace-objects log показує справжню історію, не змінюючи жодного налаштування.

Коли братися за це замість переписування

Мета Інструмент
Клон завеликий, з історією все гаразд Графт — нічого не переписано, оборотно
Секрет чи величезний блоб мають зникнути Видалення файлу з історії — справжнє переписування
Просто хочеться менше качати один раз git clone --depth — поверхнево, жодних рефів для догляду

Графт нічого не видаляє. Якщо причина, з якої вам треба прибрати старі коміти, у тому, що вони містять щось, чого ніколи не мало бути закомічено, — це не та сторінка: об’єкти й далі на місці, й далі доступні за sha, й далі в кожному наявному клоні.

Обмеження, які варто знати

  • Те, що ви бачите, перестає збігатися з тим, що збережено. Це і є фіча, і це ж і небезпека. Кожен, хто налагоджує клон із замінами, має знати, що вони є.
  • Заміни за замовчуванням не подорожують, тож git log колеги і ваш можуть цілком законно не погоджуватися.
  • Заміна може сховати коміт від інструментів, але не від git. git cat-file і оглядач об’єктів все одно відкриють оригінал за sha.
  • Gitcito не пропонує git replace --edit (редагування вмісту об’єкта руками). Це робота текстового редактора над сирим об’єктом, і граблі, навколо яких намалювали UI.

Дивіться також: Оглядач об’єктів · Видалення файлу з історії · Обслуговування репозиторію

Покращити цей переклад на GitHub