Розбийте попередню комісію на кілька комітетів


1222

Не створюючи філії та не виконуючи купу прикольних робіт над новою гілкою, чи можливо розбити один комітет на кілька різних комітетів після того, як він був докладений до локального сховища?


36
Хорошим джерелом для того, як дізнатися, як це зробити, є Pro Git §6.4 Інструменти Git - Переписування історії , в розділі "Розбиття комітету".

2
Документи, зв'язані в коментарі вище, чудові, краще пояснені, ніж відповіді нижче.
Blaisorblade

2
Я пропоную використовувати цей псевдонім stackoverflow.com/a/19267103/301717 . Це дозволяє розділити git autorebase split COMMIT_ID
комісію

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

1
Я зіткнувся з цією проблемою після того, як під час повторної роботи я випадково розбив два коміти під час перезавантаження. Мій спосіб , щоб виправити це було перевірка стислу зробити, git reset HEAD~, git stash, то git cherry-pickпершим зробити в сквош, а потім git stash pop. Мій вишневий вибір разі досить специфічні тут, але git stashі git stash popдуже зручно для інших.
SOFe

Відповіді:


1797

git rebase -i зробимо це.

По-перше, почніть з чистого робочого каталогу: не git statusповинно відображатися жодних змін, видалень чи доповнень, що очікують на очікування.

Тепер ви маєте вирішити, які (або) зобов'язання (я) потрібно розділити.

A) Розбиття останнього комітету

Щоб розділити останню комісію, спочатку:

$ git reset HEAD~

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

B) Розщеплення комірки далі назад

Це вимагає перебазування , тобто, переписування історії. Щоб знайти правильну фіксацію, у вас є кілька варіантів:

  • Якщо це було три коміти назад, то

    $ git rebase -i HEAD~3
    

    де 3це кількість комісій назад.

  • Якщо це було далі на дереві, ніж ви хочете порахувати, то

    $ git rebase -i 123abcd~
    

    де 123abcdSHA1 комітету, який ви хочете розділити.

  • Якщо ви перебуваєте в іншій гілці (наприклад, спеціалізованій гілці), яку плануєте об'єднати в головний:

    $ git rebase -i master
    

Коли ви отримаєте екран редагування бази даних, знайдіть комісію, яку ви хочете розірвати. На початку цього рядка замініть pickна edit( eкоротко). Збережіть буфер і вийдіть. Тепер база даних зупиниться відразу після виконання комісії, яку ви хочете відредагувати. Тоді:

$ git reset HEAD~

Виконайте деталі окремо звичайним способом, тоді ви зробите стільки завдань, скільки вам потрібно

$ git rebase --continue

2
Дякую за цю відповідь. Мені хотілося, щоб у зоні інсценізації були створені раніше файли, тому вказівки для мене були дещо іншими. Перш ніж я міг би git rebase --continue, я на самому справі був git add (files to be added), git commit, а потім git stash(для інших файлів). Після цього git rebase --continueя використовував git checkout stash .файли, що залишилися
Ерік Ху

18
Відповідь manojlds насправді має це посилання на документацію на git-scm , що також дуже чітко пояснює процес розбиття комітетів.

56
Ви також захочете скористатись лише git add -pтим, що додаєте лише часткові розділи файлів, можливо, з eможливістю редагування розрізних файлів виконувати лише частину запитів. git stashтакож корисно, якщо ви хочете перенести якусь роботу вперед, але видалити її з поточної комісії.
Крейг Рінгер

2
Якщо ви хочете розділити і упорядкувати комісії, те, що я люблю робити, - це розділити спочатку, а потім змінити порядок окремо за допомогою іншої git rebase -i HEAD^3команди. Таким чином, якщо розкол піде не так, вам не доведеться скасовувати стільки роботи.
Девід М. Ллойд

4
@kralyk Файли, які були нещодавно зроблені в HEAD, залишаться на диску після git reset HEAD~. Вони не загублені.
Уейн Конрад

312

З посібника з git- rebase (розділ SPLITTING COMMITS)

В інтерактивному режимі ви можете позначати комісії дією "редагувати". Однак це не обов'язково означає, що git rebase очікує, що результат цього редагування буде рівно однією фіксацією. Дійсно, ви можете скасувати комісію або додати інші коміти. Це можна використовувати для поділу комісії на два:

  • Запустіть інтерактивну базу даних із пунктом git rebase -i <commit>^, <commit>який ви хочете розділити. Насправді, будь-який діапазон комісій буде виконуватись, доки він містить цю комісію.

  • Позначте команду, яку ви хочете розділити, дією "редагувати".

  • Що стосується редагування цього коміту, виконайте його git reset HEAD^. Ефект полягає в тому, що HEAD перемотується на одиницю, а індекс іде відповідно. Однак робоче дерево залишається таким же.

  • Тепер додайте зміни до індексу, який ви хочете мати під час першого виконання. Ви можете використовувати це git add(можливо, інтерактивно) або git gui(або обидва) для цього.

  • Зв'яжіть поточний індекс із будь-яким повідомленням про фіксацію.

  • Повторіть останні два кроки, поки ваше робоче дерево не очиститься.

  • Продовжуйте оновлення з git rebase --continue.


12
У Windows ви використовуєте ~замість ^.
Кевін Кушик

13
Слово обережності: при такому підході я втратив повідомлення про вчинення.
user420667

11
@ user420667 Так, звичайно. resetЗрештою, ми приймаємо на себе зобов'язання - повідомлення включене. Якщо ви знаєте, що ви збираєтеся розділити зобов'язання, але хочете зберегти частину / все його повідомлення, це зробити копію цього повідомлення. Отже, виконайте git showкоманду перед rebaseing, або якщо ви забудете або віддасте перевагу цьому: поверніться до неї пізніше через reflog. Ніщо з цього фактично не буде "загублене", поки його не збирають сміття через 2 тижні чи що завгодно.
підкреслюй_

4
~і ^це різні речі навіть у Windows. Ви все ще хочете карети ^, тому вам просто потрібно буде уникнути її, як підходить для вашої оболонки. У PowerShell це так HEAD`^. За допомогою cmd.exe ви можете подвоїти його, щоб сховатися HEAD^^. У більшості (усіх?) Оболонок можна оточити цитатами на кшталт "HEAD^".
AndrewF

7
Ви також можете зробити git commit --reuse-message=abcd123. Короткий варіант для цього є -C.
j0057

41

Використовуйте, git rebase --interactiveщоб відредагувати цю попередню фіксацію, запустіть git reset HEAD~, а потім git add -pдодайте деякі, потім зробіть зобов’язання, потім додайте ще кілька і зробіть ще одне, скільки завгодно разів. Коли ви закінчите, запустіть git rebase --continue, і ви отримаєте всі розділення , які виконуються раніше, у вашому стеку.

Важливо : Зауважте, що ви можете пограти і внести всі потрібні зміни, і не потрібно турбуватися про втрату старих змін, адже ви завжди можете запустити, git reflogщоб знайти точку у вашому проекті, яка містить потрібні вам зміни (назвемо це a8c4ab) , а потім git reset a8c4ab.

Ось низка команд, щоб показати, як це працює:

mkdir git-test; cd git-test; git init

тепер додайте файл A

vi A

додати цей рядок:

one

git commit -am one

потім додайте цей рядок до A:

two

git commit -am two

потім додайте цей рядок до A:

three

git commit -am three

тепер файл A виглядає так:

one
two
three

а наш git logвиглядає наступним чином (ну, я використовуюgit log --pretty=oneline --pretty="%h %cn %cr ---- %s"

bfb8e46 Rose Perrone 4 seconds ago ---- three
2b613bc Rose Perrone 14 seconds ago ---- two
9aac58f Rose Perrone 24 seconds ago ---- one

Припустимо, ми хочемо розділити другу зробити, two.

git rebase --interactive HEAD~2

Це виводить повідомлення, яке виглядає приблизно так:

pick 2b613bc two
pick bfb8e46 three

Змініть перший pickна на, eщоб відредагувати цей комікт.

git reset HEAD~

git diff показує нам, що ми просто зняли зобов’язання, яке ми зробили для другого зобов'язання:

diff --git a/A b/A
index 5626abf..814f4a4 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two

Давайте поетапно змінимо та додамо "та третю" до цього рядка у файлі A.

git add .

Це, як правило, під час інтерактивної бази даних, куди ми б бігли git rebase --continue, тому що ми зазвичай просто хочемо повернутися в наш стек комітетів, щоб відредагувати більш ранню фіксацію. Але цього разу ми хочемо створити новий комітет. Тож ми біжимо git commit -am 'two and a third'. Тепер ми редагуємо файл Aі додаємо рядок two and two thirds.

git add . git commit -am 'two and two thirds' git rebase --continue

Ми маємо конфлікт з нашими зобов'язаннями three, тому давайте вирішимо:

Ми змінимось

one
<<<<<<< HEAD
two and a third
two and two thirds
=======
two
three
>>>>>>> bfb8e46... three

до

one
two and a third
two and two thirds
three

git add .; git rebase --continue

Зараз наше git log -pвиглядає так:

commit e59ca35bae8360439823d66d459238779e5b4892
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:57:00 2013 -0700

    three

diff --git a/A b/A
index 5aef867..dd8fb63 100644
--- a/A
+++ b/A
@@ -1,3 +1,4 @@
 one
 two and a third
 two and two thirds
+three

commit 4a283ba9bf83ef664541b467acdd0bb4d770ab8e
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:07:07 2013 -0700

    two and two thirds

diff --git a/A b/A
index 575010a..5aef867 100644
--- a/A
+++ b/A
@@ -1,2 +1,3 @@
 one
 two and a third
+two and two thirds

commit 704d323ca1bc7c45ed8b1714d924adcdc83dfa44
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:06:40 2013 -0700

    two and a third

diff --git a/A b/A
index 5626abf..575010a 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two and a third

commit 9aac58f3893488ec643fecab3c85f5a2f481586f
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:56:40 2013 -0700

    one

diff --git a/A b/A
new file mode 100644
index 0000000..5626abf
--- /dev/null
+++ b/A
@@ -0,0 +1 @@
+one

38

Попередні відповіді охоплювали використання git rebase -iдля редагування комітету, який ви хочете розділити, та виконання його частинами.

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

Діставшись зобов’язання, яке ви хочете розділити, використовуючи rebase -iта позначивши його edit, у вас є два варіанти.

  1. Після використання git reset HEAD~перейдіть патчі окремо, використовуючи git add -pдля вибору потрібні елементи під час кожного виконання

  2. Відредагуйте робочу копію, щоб видалити не потрібні зміни; вчинити цей тимчасовий стан; а потім витягнути повну комісію для наступного раунду.

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

Після використання rebase -iта editвикористання комісії, використовуйте

git reset --soft HEAD~

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

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

Після того, як ви щасливі, розмістіть / видаліть файли за потребою (я люблю це використовувати git gui) та виконайте зміни через інтерфейс користувача або командний рядок

git commit

Це перша зроблена комісія. Тепер ви хочете відновити свою робочу копію в тому стані, який він мав після розколу, який ви розщеплюєте, щоб ви могли скористатися більшою кількістю змін для наступного вчинення. Щоб знайти sha1 комітету, який ви редагуєте, використовуйте git status. У перших кількох рядках статусу ви побачите команду rebase, яка виконується в даний час, і в якій ви можете знайти sha1 свого початкового комітету:

$ git status
interactive rebase in progress; onto be83b41
Last commands done (3 commands done):
   pick 4847406 US135756: add debugging to the file download code
   e 65dfb6a US135756: write data and download from remote
  (see more in file .git/rebase-merge/done)
...

У цьому випадку команда, яку я редагую, має sha1 65dfb6a. Знаючи це, я можу перевірити вміст цього комітету в моєму робочому каталозі, використовуючи форму, git checkoutяка займає і фіксацію, і розташування файлу. Тут я використовую .як розташування файлів, щоб замінити всю робочу копію:

git checkout 65dfb6a .

Не пропустіть крапку в кінці!

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

Ви можете або продовжити зараз, і виконати це як - це закінчити розкол, або знову обійтися, видаливши деякі частини зобов’язання перед тим, як зробити чергове тимчасове зобов'язання.

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

git commit --file .git/rebase-merge/message

Нарешті, після внесення всіх змін,

git rebase --continue

буде продовжувати та завершувати операцію ребаші.


3
Дякую!!! Це має бути прийнятою відповіддю. Врятував би мені багато часу і болю сьогодні. Це єдина відповідь, коли результат остаточного зобов’язання приводить вас у той самий стан, що і команда в редагуванні.
Doug Coburn

1
Мені подобається, як ви використовуєте оригінальне повідомлення про фіксацію.
Саламандар

Використовуючи варіант 2, коли я git checkout *Sha I'm Editing* .це роблю, він завжди говорить Updated 0 paths from *Some Sha That's Not In Git Log*і не дає змін.
Номенон

18

git rebase --interactiveможна використовувати для розділення комітету на менші коміти. Документи Git на ребазі мають стисле проходження процесу - Розщеплення комітетів :

В інтерактивному режимі ви можете позначати комісії дією "редагувати". Однак це не обов'язково означає, що git rebaseочікується, що результат цього редагування буде саме одним викликом. Дійсно, ви можете скасувати комісію або додати інші коміти. Це можна використовувати для поділу комісії на два:

  • Запустіть інтерактивну базу даних із пунктом git rebase -i <commit>^, <commit>який ви хочете розділити. Насправді, будь-який діапазон комісій буде виконуватись, доки він містить цю комісію.

  • Позначте команду, яку ви хочете розділити, дією "редагувати".

  • Що стосується редагування цього коміту, виконайте його git reset HEAD^. Ефект полягає в тому, що HEAD перемотується на одиницю, а індекс іде відповідно. Однак робоче дерево залишається таким же.

  • Тепер додайте зміни до індексу, який ви хочете мати під час першого виконання. Ви можете використовувати це git add(можливо, інтерактивно) або git gui (або обидва) для цього.

  • Зв'яжіть поточний індекс із будь-яким повідомленням про фіксацію.

  • Повторіть останні два кроки, поки ваше робоче дерево не очиститься.

  • Продовжуйте оновлення з git rebase --continue.

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


У Windows пам’ятайте ^, що це символ втечі для командного рядка: його слід подвоїти. Наприклад, видайте git reset HEAD^^замість git reset HEAD^.
Фредерік

@ Frédéric: s Я ніколи не стикався з цим. Принаймні, у PowerShell це не так. Потім за допомогою ^двічі скидає два коміти вище поточної HEAD.
Farway

@Farway, спробуйте це в класичному командному рядку. PowerShell - це зовсім інший звір, його втікаючим характером є задній край.
Фредерік

Підсумовуючи: "HEAD^"у cmd.exe або PowerShell, HEAD^^у cmd.exe, HEAD`^в PowerShell. Корисно дізнатися про те, як працюють оболонки - і ваша конкретна оболонка (тобто, як команда перетворюється на окремі частини, які передаються програмі), щоб ви могли адаптувати команди в Інтернеті під потрібні символи для вашої конкретної оболонки. (Не стосується Windows.)
AndrewF

11

Тепер в останньому TortoiseGit в Windows ви можете це зробити дуже легко.

Відкрийте діалогове вікно відновлення, налаштуйте його та виконайте наступні дії.

  • Клацніть правою кнопкою миші команду, яку ви хочете розділити, і виберіть " Edit" (серед вибору, сквош, видалення ...).
  • Натисніть " Start", щоб розпочати повторне завантаження.
  • Як тільки він прийде до зобов'язання розділити, перевірте кнопку " Edit/Split" і натисніть на " Amend" безпосередньо. Відкриється діалогове вікно фіксації.
    Змінити / розділити комісію
  • Зніміть виділення файлів, які ви хочете поставити на окрему комісію.
  • Відредагуйте повідомлення про фіксацію та натисніть " commit".
  • Поки не буде файлів для фіксації, діалогове вікно фіксації відкриватиметься знову і знову. Коли немає більше файлів для фіксації, він все одно запитає, чи хочете ви додати ще одну комісію.

Дуже корисно, дякую TortoiseGit!



8

Зверніть увагу, є також git reset --soft HEAD^. Він схожий на git reset(який за замовчуванням --mixed), але він зберігає вміст індексу. Так що якщо ви додали / вилучили файли, ви вже є їх у індексі.

Виявляється, дуже корисно у випадку гігантських доручень.


3

Ось як розділити один комітет у IntelliJ IDEA , PyCharm , PhpStorm тощо

  1. У вікні журналу контролю версій виберіть команду, яку ви хочете розділити, клацніть правою кнопкою миші та виберіть пункт Interactively Rebase from Here

  2. позначте ту, яку ви хочете розділити edit, клацнітьStart Rebasing

  3. Ви повинні побачити, що розміщений жовтий тег, що означає, що HEAD встановлений для цього фіксу. Клацніть правою кнопкою миші на цю комітку, виберітьUndo Commit

  4. Тепер ці зобов'язання повернулися до місця постановки, а потім ви можете зробити їх окремо. Після того, як всі зміни були здійснені, стара фіксація стає неактивною.


2

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


згідно стандартів SO сьогодні, це слід кваліфікувати як невідповідь; але це все ще може бути корисним для інших, тому, якщо ви не заперечуєте, будь ласка, перенесіть це до коментарів оригінальної публікації
YakovL

@YakovL Здається розумним. Що стосується принципу мінімальної дії, я не видалю відповідь, але я не заперечую, якщо це робить хтось інший.
Вільям Перселл

це було б набагато простіше, ніж усі rebase -iпропозиції. Я думаю, що це не приділяло великої уваги через відсутність форматування. Можливо, ви можете це переглянути, тепер, коли у вас є 126k балів і, ймовірно, ви знаєте, як це зробити. ;)
erikbwork


1

Якщо у вас є це:

A - B <- mybranch

Якщо ви здійснили деякий вміст у програмі B:

/modules/a/file1
/modules/a/file2
/modules/b/file3
/modules/b/file4

Але ви хочете розділити B на C - D і отримати такий результат:

A - C - D <-mybranch

Ви можете поділити такий вміст, наприклад, наприклад (вміст з різних каталогів у різних комісіях) ...

Скиньте гілку назад до комітету перед поділом:

git checkout mybranch
git reset --hard A

Створіть першу фіксацію (C):

git checkout B /modules/a
git add -u
git commit -m "content of /modules/a"

Створіть другу комісію (D):

git checkout B /modules/b
git add -u
git commit -m "content of /modules/b"

Що робити, якщо є коміти вище B?
CoolMind

1

Минуло більше 8 років, але, можливо, хтось вважатиме це корисним. Я змогла зробити трюк без цього rebase -i. Ідея полягає в тому, щоб привести git до того самого стану, який був раніше git commit:

# first rewind back (mind the dot,
# though it can be any valid path,
# for instance if you want to apply only a subset of the commit)
git reset --hard <previous-commit> .

# apply the changes
git checkout <commit-you-want-to-split>

# we're almost there, but the changes are in the index at the moment,
# hence one more step (exactly as git gently suggests):
# (use "git reset HEAD <file>..." to unstage)
git reset

Після цього ви побачите це блискуче, Unstaged changes after reset:і ваше репо буде в такому стані, як ви збираєтеся зробити всі ці файли. Відтепер ви можете легко зробити це знову, як зазвичай. Сподіваюся, це допомагає.


0

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

git rebase -i <sha1_before_split>
# mark the targeted commit with 'edit'
git reset HEAD^
git add ...
git commit -m "First part"
git add ...
git commit -m "Second part"
git rebase --continue

Подяки до публікації в блозі Еммануеля Бернарда .

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