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


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.