Як встановити зобов’язання між деякими двома довільними вчинками у минулому?


126

Припустимо, я маю таку історію фіксацій у моїй філії лише для місцевих:

A -- B -- C

Як вставити нову комісію між Aі B?


3
скоєння називається Ą ? :)
— Antek

У мене дуже схоже запитання про те, як вставити нову версію в минуле замість комітету.
— Павло П

Відповіді:


178

Це навіть простіше, ніж у відповіді ОП.

  1. git rebase -i <any earlier commit>. Тут відображається список комісій у налаштованому текстовому редакторі.
  2. Знайдіть команду, яку ви хочете вставити після (припустимо, це так a1b2c3d). У цьому редакторі для цього рядка змініть pickна edit.
  3. Почніть оновлення, закривши текстовий редактор (збережіть зміни). Це залишає вас в командному рядку з командою, яку ви вибрали раніше ( a1b2c3d), як ніби вона щойно була здійснена .
  4. Внесіть зміни та git commit( НЕ виправляйте зміни, на відміну від більшості edit). Це створює нове зобов’язання після обраного вами.
  5. git rebase --continue. Це повторює послідовні зобов’язання, залишаючи нове зобов’язання вставленим у потрібне місце.

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


1
Це додало нове зобов’язання після фіксації, яке є після того, на яке я перезапускав (також останній фільтр), а не відразу після того, на який я відновлював. Результат був таким же, як якщо б я просто зробив нове зобов’язання наприкінці із змінами, які хотів внести. Моя історія стала A -- B -- C -- Dзамість бажаної A -- D -- B -- C.
— XedinНевідомо

2
@XedinUnknown: Тоді ви не використовували Rebase належним чином.
— СЛАкс

3
Тепер це Dможе бути зобов’язання будь-де. Припустимо, у нас є A - B - Cі є деякі зобов'язання, Dяких немає навіть у цій галузі. Ми знаємо його SHA, проте ми можемо це зробити git rebase -i HEAD~3. Тепер між рядками Aта B pickрядками ми вставляємо новий pick рядок, який говорить pick SHA, надаючи хеш потрібного D. Це не повинен бути повний хеш, просто укорочений. git rebase -iпросто вишня вибирає будь-які комісії, перелічені pickрядками в буфері; вони не повинні бути оригінальними, які він перелічив для вас.
— Каз

1
@Kaz Це схоже на іншу, дійсну відповідь.
— BartoszKP

3
Ще простіше, ви можете використовувати breakключове слово в редакторі в його власному рядку між двома комітами (або в першому рядку, щоб вставити фіксацію перед вказаною фіксацією).
— SimonT

30

Виявляється, тут досить просто, відповідь знайдено тут . Припустимо, ти на гілці branch. Виконайте такі дії:

  • створіть тимчасову гілку з комітету після того, як ви хочете вставити нову комісію (у цьому випадку виконувати A):

    git checkout -b temp A
    
  • виконайте зміни та виконайте їх, створивши фіксацію, назвемо це N:

    git commit -a -m "Message"
    

    (або git addсупроводжується git commit)

  • відновіть зобов’язання, які ви хочете мати після нової фіксації (у цьому випадку здійснює Bта C) на нову комісію:

    git rebase temp branch
    

(можливо, вам потрібно використовувати -pдля збереження злиття, якщо вони були - завдяки вже не існуючому коментарю від ciekawy )

  • видалити тимчасову гілку:

    git branch -d temp
    

Після цього історія виглядає так:

A -- N -- B -- C

Звичайно, можливо, деякі конфлікти з'являться під час відмови.

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


2
Я не міг дотримуватися прийнятої відповіді SLaks, але це працювало на мене. Після того, як я отримав потрібну історію фіксів, мені довелося git push --forceзмінити віддалений репо.
— escapecharacter

1
При використанні ребазу за допомогою параметра -Xtheirs автоматично вирішує конфлікти правильно, так git rebase temp branch -Xtheirs. Корисна відповідь за введення в сценарій!
— Девід С

Для ноубів, як я, я хотів би додати це після git rebase temp branch, але раніше git branch -d temp, все, що вам потрібно зробити, - це виправити та встановити злиття конфліктів і видавати питання git rebase --continue, тобто не потрібно нічого робити, і т. Д.
— Пугслі,

19

Ще простіше рішення:

  1. Створіть свою нову комісію наприкінці, D. Тепер у вас є:

    A -- B -- C -- D
    
  2. Потім запустіть:

    $ git rebase -i hash-of-A
    
  3. Git відкриє ваш редактор, і це виглядатиме так:

    pick 8668d21 B
    pick 650f1fc C
    pick 74096b9 D
    
  4. Просто перемістіть D на верхню частину так, а потім збережіть і вийдіть

    pick 74096b9 D
    pick 8668d21 B
    pick 650f1fc C
    
  5. Тепер у вас буде:

    A -- D -- B -- C
    

6
Хороша ідея, проте може бути важко ввести D на C, коли ви маєте намір ці зміни бути wrt. до А.
— BartoszKP

У мене є ситуація, коли у мене є 3 коміти, які я хочу перезавантажити разом, і виконувати зобов'язання посередині, що не пов'язане між собою. Це дуже приємно, щоб можна було просто перемістити цю команду раніше чи пізніше вниз по лінії комітетів.
— розблоковується

13

Якщо припустити, що історія комісій є preA -- A -- B -- C, якщо ви хочете вставити комітку між Aі B, кроки наступні:

  1. git rebase -i hash-of-preA

  2. Git відкриє ваш редактор. Зміст може виглядати так:

    pick 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Змініть перший pickна edit:

    edit 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Зберегти та вийти.

  3. Змініть код, а потім git add . && git commit -m "I"

  4. git rebase --continue

Тепер ваша історія фіксації Git є preA -- A -- I -- B -- C


Якщо у вас виник конфлікт, Git зупиниться на цьому. Ви можете використовувати git diffдля пошуку маркерів конфлікту та їх вирішення. Вирішивши всі конфлікти, потрібно git add <filename>сказати Git, що конфлікт вирішено, а потім повторно git rebase --continue.

Якщо ви хочете скасувати відновлення бази даних, використовуйте git rebase --abort.


10

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

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

break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>

Після запуску, git rebaseтепер зупиниться на точці "перерви". Тепер ви можете редагувати свої файли та створювати свої зобов’язання. Потім ви можете продовжити оновлення за допомогою git rebase --continue. Це може спричинити конфлікти, які вам доведеться виправити. Якщо ви загубитесь, не забувайте, що завжди можете перервати вживанняgit rebase --abort .

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

Після переписування історії не забудьте git push -f. Діють звичайні попередження про інших людей, які отримують вашу гілку.


Вибачте, але у мене проблеми з розумінням, як це "уникнення перезавантаження". Ви які працює rebaseтут. Не велика різниця в тому, чи будете ви створювати фіксацію під час перезавантаження або попередньо.
— BartoszKP

Woops, я мав на увазі уникнути "редагування зламу" під час перезавантаження, я думаю, що я погано сформулював це.
— axerologementy

Правильно. Моя відповідь також не використовує функцію "редагування" ребазу. І все-таки це ще один вагомий підхід - дякую! :-)
— BartoszKP

6

Тут вже багато хороших відповідей. Я просто хотів додати рішення "без перезавантаження" за 4 простих кроки.


Підсумок

git checkout A
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD

Пояснення

(Примітка. Однією з переваг цього рішення є те, що ви не торкаєтесь своєї філії до останнього кроку, коли ви на 100% впевнені, що ви готові з кінцевим результатом, тому у вас дуже зручний крок "попереднього підтвердження" що дозволяє проводити тестування AB .


Початковий стан (я припустив masterназву вашої філії)

A -- B -- C <<< master <<< HEAD

1) Почніть з вказівки на голову в потрібне місце

git checkout A

     B -- C <<< master
    /
   A  <<< detached HEAD

(Можливо, тут, замість того, щоб роз'єднувати HEAD, ми могли б створити тимчасову гілку git checkout -b temp A, яку нам потрібно було б видалити в кінці процесу. Обидва варіанти працюють, як вам зручніше, оскільки все інше залишається незмінним)


2) Створіть нову комісію D, яку потрібно вставити

# at this point, make the changes you wanted to insert between A and B, then

git commit -am "Message for commit D"

     B -- C <<< master
    /
   A -- D <<< detached HEAD (or <<< temp <<< HEAD)

3) Потім принесіть копії останніх відсутніх комітетів B і C (був би той самий рядок, якби було більше комісій)

git cherry-pick A..C

# (if any, resolve any potential conflicts between D and these last commits)

     B -- C <<< master
    /
   A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)

(зручне тестування AB тут, якщо потрібно)

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


4) Залежно від ваших тестів між Cі C', або це нормально, або це КО.

(ВСЕ) 4-ОК) Нарешті, перемістіть посилання наmaster

git branch -f master HEAD

     B -- C <<< (B and C are candidates for garbage collection)
    /
   A -- D -- B' -- C' <<< master

(АБО) 4-КО) Просто залиште masterбез змін

Якщо ви створили тимчасову гілку, просто видаліть її git branch -d <name>, але якщо ви перейшли на відокремлений маршрут HEAD, то в цей момент ніяких дій взагалі не потрібно, нові комісії будуть придатними для збору сміття відразу після того, як ви повторно HEADвстановитеgit checkout master

В обох цих випадках (ОК або КО), у цей момент просто masterповторіть перевірку, щоб повторно встановити HEAD.

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