Як видалити певну редакцію в історії git?


213

Припустимо, ваша історія git виглядає приблизно так:

1 2 3 4 5

1–5 - це окремі зміни. Вам потрібно видалити 3, зберігаючи 1, 2, 4 і 5. Як це можна зробити?

Чи є ефективний метод, коли після видалення є сотні версій?


Це питання недостатньо визначене. Це прямо не говорить про те, що автор хоче 1-2- (3 + 4) -5 або 1-2-4-5
RandyTek

14
Ну 8 років потому я не можу точно сказати, яку проблему я намагався вирішити. Але в git завжди є багато способів зробити щось, і є багато відповідей, які сподобалися різним людям, тому я гадаю, що двозначність не спричиняє занадто багато людей чимало труднощів
1800 ІНФОРМАЦІЯ

1
git rebase --onto 2 3 HEADв значній мірі означає перебазування на 2, з комами між 3 та HEAD (HEAD є необов’язковим, або 5 в цьому випадку)
неаумузичний

Відповіді:


76

Щоб поєднати версію 3 і 4 в одну редакцію, ви можете використовувати git rebase. Якщо ви хочете видалити зміни у версії 3, вам потрібно скористатися командою редагування в режимі інтерактивного ребазування. Якщо ви хочете об'єднати зміни в одну редакцію, використовуйте сквош.

Я успішно використовував цю техніку сквош, але ніколи не потребував видалення ревізії. Документація щодо git-rebase у розділі "Розщеплення комітетів", сподіваємось, дасть вам достатньо ідеї, щоб зрозуміти це. (Або хтось інший може знати).

З документації по git :

Почніть це з найдавнішої комісії, яку ви хочете зберегти як є:

git rebase -i <after-this-commit>

Редактор буде звільнений від усіх комісій у вашій поточній галузі (ігноруючи об'єднання об'єднань), які настають після даної фіксації. Ви можете змінити порядок у цьому списку на вміст вашого серця, а ви можете їх видалити. Список виглядає більш-менш так:

вибрати мертву бджолу Одне рядок цього зобов'язання
вибрати fa1afe1 Один рядок наступного комітету
...

Описи в одній лінії призначені виключно для вашого задоволення; git-rebase не буде дивитись на них, а на імена комітів ("deadbee" та "fa1afe1" у цьому прикладі), тому не видаляйте та не редагуйте імена.

Замінивши команду "вибрати" командою "редагувати", ви можете сказати git-rebase припинити після застосування цього комітету, щоб ви могли редагувати файли та / або повідомлення про фіксацію, вносити зміни до комісії та продовжувати повторне використання.

Якщо ви хочете скласти два чи більше доручень на один, замініть команду "pick" на "squash" на другу та наступну фіксацію. Якщо у комітетів були різні автори, це приписує розбиту комітку автору першого комітету.


42
-1 Питання чітко визначене, але ця відповідь не така однозначна. Автор не каже, яке саме рішення.
Олександр Левчук

1
Це неправильна ведуча. Розділ СПІЛЬНИХ КОМІТІВ не є правильним. Ви хочете прочитати набагато вище в посібнику - дивіться відповідь @Rares Vernica.
Олександр Левчук

1
@AleksandrLevchuk Питання не є чітко визначеним: незрозуміло з того, яким чином задається питання, чи слід зберігати чи відміняти набір змін у 3. Я погоджуюся, що якщо зміни потрібно відмінити, інші відповіді пропонують більш простий підхід. Якщо зміни дотримуватимуться, це була б чисто косметична операція. Обидва підходи переписують історію небезпечними способами, якщо інші, можливо, ґрунтуються на роботі над помилковою історією; якщо це так, косметичну чистку не слід проводити, а видалення змін краще зробити за допомогою git revert.
Теодор Мердок

124

Відповідно до цього коментаря (і я перевірив, що це правда), відповідь Радо дуже близький, але залишає git у відокремленій голові. Замість цього видаліть HEADі скористайтеся цим для видалення <commit-id>з гілки, на якій ви перебуваєте:

git rebase --onto <commit-id>^ <commit-id>

1
Якби ви могли сказати мені, як також видалити цей ідентифікатор комітету з історії, заснований на просто ідентифікаторі команд, ви стали моїм героєм.
kayleeFrye_onDeck

Чи можете ви включити пояснення, що робить магічна команда у відповідь? Тобто, що позначає кожен параметр?
Олександроїд

це дивовижно, але що робити, якщо я хочу зняти першу фіксацію в історії? (через що я прийшов сюди: P)
Стерлінг Камден

2
Чомусь, коли я запускаю це, нічого не відбувається. Однак, змінившись, ^щоб ~1це спрацювало.
Сурма

Шлях пізно, але я додам, що якщо ви зіткнетеся з конфліктами злиття, перервіть базу даних, а потім повторіть її з --strategy-option theirsкінця.
LastStar007

122

Ось спосіб видалити неінтерактивне певне <commit-id>, знаючи лише те, що <commit-id>ви хочете видалити:

git rebase --onto <commit-id>^ <commit-id> HEAD

2
Працював і для мене. Btw, що робить ^ оператор? Чи означає це наступне вчинення після зазначеного?
hopia

3
@hopia це означає (перший) батько вказаного коміту. Дивіться "Довідки щодо допомоги git"
Еміль Стірке

12
Дивіться рекомендацію @ kareem у своїй відповіді пропустити HEADтак, щоб уникнути відстороненої голови.
mklement0

1
Набагато простіше, ніж розібратися, скільки повертається на пошук.
Дана Вудман

1
це дивовижно, але що робити, якщо я хочу зняти першу фіксацію в історії? (через що я прийшов сюди: P)
Стерлінг Камден

76

Як зазначалося раніше, git-rebase (1) - ваш друг. Якщо припустити, що комісії є у ​​вашій masterфілії, ви зробите:

git rebase --onto master~3 master~2 master

Перед:

1---2---3---4---5  master

Після:

1---2---4'---5' master

З git-rebase (1):

Діапазон комітетів також може бути видалений за допомогою повторної бази. Якщо у нас є така ситуація:

E---F---G---H---I---J  topicA

потім команда

git rebase --onto topicA~5 topicA~3 topicA

призведе до вилучення комітетів F і G:

E---H'---I'---J'  topicA

Це корисно, якщо F і G якимось чином були помилковими або не повинні бути частиною темиA. Зауважте, що аргументом до --onto і параметру може бути будь-який припустимий ідентифікатор.


3
не повинно бути цього --onto master~3 master~1?
Маттіас

3
Якщо ви просто хочете видалити останню комісію, це - master master ~ 1
MikeHoss

Я хотів би перенести цю золь. але я отримую MERGE CONFLICTпомилку Я використовував сценарій, згаданий в stackoverflow.com/questions/2938301/remove-specific-commit, і я не можу видалити 2-й коміт у цьому прикладі.
maan81

22

Якщо все, що ви хочете зробити, - це усунути зміни, внесені у версії 3, ви можете скористатися git revert.

Повернення Git просто створює нову версію зі змінами, які скасовують усі зміни в редакції, яку ви ревертуєте.

Це означає, що ви зберігаєте інформацію як про небажане виконання, так і про зобов'язання, яке видаляє ці зміни.

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


4
На жаль, це не гарне рішення для мене, тому що хтось випадково здійснив 100 Мб лайно до репо, підірвавши розмір і зробивши веб-інтерфейс млявим.
Стівен Сміт

18

Усі відповіді на даний момент не стосуються неспокійної проблеми:

Чи є ефективний метод, коли після видалення є сотні версій?

Наступні кроки, але для довідки припустимо таку історію:

[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]

С : вчинити лише після виконання зобов'язання, яке потрібно видалити (очистити)

R : Комісія, яку потрібно зняти

B : фіксація, що безпосередньо передує видаленню (база)

Через обмеження "сотні змін" я припускаю такі умови:

  1. є деякі незручні вчинки, яких ви хочете, щоб ніколи не існувало
  2. є наступні зобов’язання ZERO, які фактично залежать від цього невтішного вчинення (нульові конфлікти при поверненні)
  3. тобі не байдуже, що ти будеш вказаний як "Комітер" із сотень втручаються комісій ("Автор" буде збережено)
  4. ви ніколи не ділилися сховищем
    • або ти насправді маєш достатній вплив на всіх людей, котрі коли-небудь клонували історію, що вчинили її, щоб переконати їх використати твою нову історію
    • і ви не дбаєте про переписування історії

Це досить обмежуючий набір обмежень, але є цікава відповідь, яка насправді працює в цьому кутовому випадку.

Ось такі кроки:

  1. git branch base B
  2. git branch remove-me R
  3. git branch save
  4. git rebase --preserve-merges --onto base remove-me

Якщо справді немає конфліктів, то це має продовжуватися без подальших перерв. Якщо виникають конфлікти, ви можете їх вирішити та rebase --continueабо вирішити просто жити зі збентеженням і rebase --abort.

Тепер ви повинні бути на masterтому, що більше не має R в ньому. У saveточках розгалуження, де ви були раніше, в разі , якщо ви хочете поєднати.

Як ви хочете домовитись про перенесення всіх інших до вашої нової історії, залежить від вас. Вам потрібно буде ознайомитися з stash, reset --hardі cherry-pick. І ви можете видалити base, remove-meі saveгілки


як мені це подобається 150000 разів?
Гена Мороз

Мені працювали над тим, щоб видалити 3 послідовних послідовності з середини історії відділення, де хтось здійснив купу файлів 150mb.
Маркос

3

Я також приземлився в подібній ситуації. Використовуйте інтерактивну базу даних, використовуючи команду нижче, і під час вибору відміньте 3-й коміт.

git rebase -i remote/branch

2

Отже ось сценарій, з яким я зіткнувся, і як я його вирішив.

[branch-a]

[Hundreds of commits] -> [R] -> [I]

ось Rфіксація, яку мені потрібно було зняти, і Iце єдине зобов’язання, яке відбувається післяR

Я взяв на себе зобов’язання повернутись і розбив їх разом

git revert [commit id of R]
git rebase -i HEAD~3

Під час інтерактивної бази даних сквош останні 2 коміти.


0

Відповіді rado і kareem для мене нічого не роблять (з'являється лише повідомлення "Поточна галузь актуальна".) Можливо, це трапляється тому, що символ '^' не працює в консолі Windows. Однак, згідно з цим коментарем, заміна "^" на "~ 1" вирішує проблему.

git rebase --onto <commit-id>^ <commit-id>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.