Як я можу об'єднати два коміти в один, якщо я вже розпочав перезавантаження?


1157

Я намагаюся об'єднати 2 коміти в 1, тому я перейшов до "сквош-комітів з перезавантаженням" з git ready .

Я побіг

git rebase --interactive HEAD~2

У редакторі, що виходить, я переходжу pickна, squashа потім зберігаю-виходжую, але перезавантаження не вдається з помилкою

Неможливо "сквош" без попереднього виконання

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

Команда git rebase --interactive HEAD~2не вдається:

Інтерактивна база даних вже розпочалася

і git rebase --continueне вдається з

Неможливо "сквош" без попереднього виконання


22
Я теж вразив цього. Моя помилка була викликана тим, що git rebase -i перераховує комісії у протилежному порядку git log; остання комісія знаходиться внизу!
Лемспрепарат


1
також ознайомтесь: git-scm.com/book/en/Git-Tools-Rewriting-History
nha

Відповіді:


1732

Підсумок

Повідомлення про помилку

Неможливо "сквош" без попереднього виконання

означає, що ви, ймовірно, намагалися "скосити вниз". Git завжди стискає новіші зобов'язання на більш старий або "вгору", як це переглядається в списку todo інтерактивних баз даних, тобто в команду в попередньому рядку. Зміна команди в першому рядку списку todo squashзавжди призведе до цієї помилки, оскільки немає нічого для першого виконання.

Виправлення

Спочатку поверніться туди, де ви почали

$ git rebase --abort

Скажіть, ваша історія є

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

Тобто, a було першим комітом, потім b і, нарешті, c. Після вчинення c ми вирішуємо сквош b і c:

(Примітка. Запустивши git logтруби його вихід у пейджер, lessза замовчуванням на більшості платформ. Щоб вийти з пейджера і повернутися до командного рядка, натисніть qклавішу.)

Запуск git rebase --interactive HEAD~2дає вам редактор з

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(Зауважте, що цей список тодо в зворотному порядку порівняно з результатом git log.)

Якщо змінити b pickна, squashце призведе до помилки, яку ви побачили, але якщо замість цього, ви зімкнете c на b (новіший перехід у старіший або "збиття вгору"), змінивши список todo на

pick   b76d157 b
squash a931ac7 c

і збереживши-вийшовши з редактора, ви отримаєте інший редактор, вміст якого є

# This is a combination of 2 commits.
# The first commit's message is:

b

# This is the 2nd commit message:

c

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

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

Примітка про історію переписування

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

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

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

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


Якщо я використовую rebase для розбиття комітів, створюється новий "комбінований" коміт, що містить два набори змін, але хеш - інший. Чи збереглися також оригінали комітетів git?
fabsenet

@fabsenet Так і ні. Оригінальні комісії все ще доступні, але, ймовірно, більше не доступні з будь-якої посилання (залежно від деталей вашої історії). Нереференційні комісії врешті-решт вилучаються через процес вивезення сміття.
Грег Бекон

я просто грав навколо ... я робив, git log hashoftheoldcommitі це спрацювало, але мені було цікаво побачити git log --graphз усіма цими недоступними зобов'язаннями
fabsenet

цей сквош - це гарний інструмент для організації комітетів перед натисканням, але якщо я натискаю на нього, я не можу його робити? git каже: Успішно відновлювали та оновлювали відокремлену голову.
Серхіо

Я не отримую редактора в другій інстанції, git bash, здається, заморожений в якомусь процесі. Що робити?

411

Якщо є кілька комітетів, можна скористатись git rebase -iдвома елементами.

Якщо є лише два коміти, які ви хочете об'єднати, і вони є "останніми двома", наступні команди можна використовувати для об'єднання двох комітетів в один:

git reset --soft "HEAD^"
git commit --amend

6
Який мінус у порівнянні з ребазом? Я вважаю його набагато простішим у використанні.
Guillaume86

17
Ви не можете приєднатися до довільного порядку - лише останні два коміти .
dr0i

50
@ dr0i Ви можете об'єднати стільки комітетів, скільки хочете, якщо вони є останніми X зобов’язаннями, а не десь посередині. Просто запустіть git reset --soft HEAD~10, де 10 - це кількість комітетів, які потрібно об'єднати.
fregante

2
Це можна використовувати, якщо у вас немає віддаленого набору джерела, і у вас є лише два коміти.
atedja

8
Ви також можете скинути до конкретного комітету, якщо ви не хочете порахувати, скільки їх є HEAD, використовуючи, git reset --soft 47b5c5...де 47b5c5...є ідентифікатор SHA1 комітету.
dguay

112

База даних: Вам це не потрібно:

Більш простий спосіб для найбільш частого сценарію.

В більшості випадків:

На насправді , якщо все , що ви хочете , це просто просто об'єднати кілька останніх фіксацій в один , але не потрібно drop, rewordі інші Rebase роботи.

ви можете просто зробити:

git reset --soft "HEAD~n"
  • Припускаючи , що ~nце число фіксацій м'яко зніміть фіксації (тобто ~1, ~2...)

Потім використовуйте наступну команду для зміни повідомлення про фіксацію.

git commit --amend

яка в значній мірі так само , як великої дальності squashі один pick.

І він працює для n комітетів, але не лише для двох команд, як запропоновано вище.


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

1
Припускаючи , що ~nце число фіксацій м'яко оон зафіксованої (тобто ~1, ~2, ...)
Ray

1
Що робити, якщо я хочу об'єднати не nостанні коміти, а nкомміти посередині? Чи можу я це зробити легко?
chumakoff

1
Тоді git rebase -iце те, що потрібно для squashроботи. @chumakoff
pambda

3
Тож, щоб приєднатись до nостанніх зобов’язань в одне, перше використання git reset --soft @~m, деm = n - 1
Łukasz Rajchel

55

Спочатку слід перевірити, скільки у вас є комісій:

git log

Є два статуси:

Одне полягає в тому, що є лише два коміти:

Наприклад:

commit A
commit B

(У цьому випадку ви не можете використовувати git rebase для виконання), вам потрібно виконати наступне.

$ git reset --soft HEAD^1

$ git commit --amend

Інша полягає в тому, що існує більше двох комітетів; ви хочете об'єднати комірки C і D.

Наприклад:

commit A
commit B
commit C
commit D

(за цієї умови ви можете використовувати git rebase)

git rebase -i B

І чим використовувати "сквош", щоб зробити. Решта стоншується дуже легко. Якщо ви все ще не знаєте, будь ласка, прочитайте http://zerodie.github.io/blog/2012/01/19/git-rebase-i/


Скидання --soft і commit --amend - це єдиний спосіб, який працює, якщо у вас вже є відновлення бази даних (і для цього комітету вибрано "редагувати" замість "сквош"). +1
Яцек Лач

1
Об’єднання першого та лише двох комісій у сховищі, саме мій кращий випадок :-)
Chris Huang-Leaver

1
Будь ласка, додайте, що це git push -f origin masterможе бути необхідним.
Рішабх Аграхарі

33

Якщо припустити, що ви були у власній тематичній галузі. Якщо ви хочете об'єднати останні два переклади в один і виглядати як герой, відкладіть зобов’язання безпосередньо перед тим, як ви зробили два останні коміти.

git checkout -b temp_branch HEAD^2

Потім сквош зафіксуйте іншу гілку в цій новій гілці:

git merge branch_with_two_commits --squash

Це внесе зміни, але не вчинить їх. Тож просто докладіть їх і ви закінчите.

git commit -m "my message"

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


5
Це було насправді найбільш корисною відповіддю для мене, оскільки воно не вимагало перезавантажування вручну, а натомість просто з’єднує всі комісії цілої гілки в одну команду. Дуже хороший.
Роберт

Дякую за це! Це те, як змусити git do робити, як я малюю, що в голові робиться сквош!
Мар'ян Венема

дивовижна відповідь, настільки простіша за альтернативу

Мабуть, ця відповідь не підходить для того випадку, коли aі cпотрібно об'єднатись та зберегти bтаким, яким він є.
Талха Ашраф

2
Щось змінилося в останніх версіях git? Коли я спробую першу команду ( git checkout -b combine-last-two-commits "HEAD^2") у версії git 2.17, я отримую помилку:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
mhucka

23

ви можете скасувати відновлення

git rebase --abort

і коли ви знову запустите інтерактивну команду rebase 'сквош; фіксація повинна бути нижче вибору комісії у списку


16

Я часто використовую git reset --mixed, щоб повернути базову версію до декількох комітетів, які ви хочете об'єднати, тоді я роблю нове зобов’язання, таким чином, ви можете дозволити вашій новій версії, переконайтеся, що ваша версія HEAD після натискання на сервер.

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

Якщо я хочу об'єднати голову два коміти в одну, спершу використовую:

git reset --mixed 249cf9392da197573a17c8426c282

"249cf9392da197573a17c8426c282" була третьою версією, також є вашою базовою версією до того, як ви з’єднаєтесь, після цього я беру новий зобов'язання:

git add .
git commit -m 'some commit message'

Це все, надія - це ще один спосіб для всіх.

FYI, від git reset --help:

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.

Я не читав документи для "--mixed", але впевнений, що інші люди читають публікацію і запитували те саме: Яка перевага використання --mixed? Ви можете покращити свою публікацію, щоб включити фрагмент сторінки чоловіка.
лінійка

@funroll Я не знав - дуже змішаний, перш ніж написати цю відповідь, згідно з власним досвідом, змішана операція перетворить вказану версію, яку я передаю як аргумент як версію HEAD-сховища, і нічого не можна втратити після цієї версії, тому ми все ще можемо впоратися з цими змінами.
VinceStyling

14

$ git rebase --abort

Запустіть цей код у будь-який час, якщо ви хочете скасувати базу даних git

$ git rebase -i HEAD~2

Щоб повторно застосувати останні два коміти. Вищевказана команда відкриє редактор коду

  • [ Остання фіксація буде внизу ]. Змініть останнє зобов’язання на сквош (и). Оскільки сквош буде поєднуватися з попередніми фіксаціями.
  • Потім натисніть клавішу esc та введіть: wq, щоб зберегти та закрити

Після: wq ви будете в активному режимі ребазування

Примітка . Якщо у вас з’явиться інший редактор, якщо жодних повідомлень про попередження / помилку не з’явиться, або попередження іншого редактора не з’явиться, ви можете перервати, запустившись, $ git rebase --abortякщо побачите помилку або попередження, просто продовжте, запустивши$ git rebase --continue

Ви побачите своє повідомлення про 2 фіксації. Виберіть одне або напишіть власне повідомлення про фіксацію, збережіть і вийдіть [: wq]

Примітка 2. Можливо, вам доведеться змусити натиснути зміни на віддалене репо, якщо ви запустите команду rebase

$ git push -f

$ git push -f origin master


1
Примітка 2: git push -f origin/masterце те, що відсутні інші відповіді. +1
Рішабх Аграхарі

2

Оскільки я використовую git cherry-pickмайже все, мені природно це робити навіть тут.

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

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

Якщо я хочу також оновити branchX(і я вважаю, що це нижня сторона цього методу), я також повинен:

git checkout branchX
git reset --hard <the_new_commit>

1

Якщо ваша головна галузь git logвиглядає приблизно так:

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

і ви хочете об'єднати два найпопулярніші документи, просто виконайте наступні прості дії:

  1. Перший знаходиться в безпечному огляді, другий останній вчиняється в окремому відділенні. Ви можете назвати відділення що завгодно.git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. Тепер, тільки вишневого забрати ваші зміни від останньої фіксації в цій новій галузі , як: git cherry-pick -n -x ac72a4308ba70cc42aace47509a5e. (Вирішуйте конфлікти, якщо такі виникають)
  3. Отже, зміни вашої останньої комісії є у ​​вашій другій останній комісії. Але ви все одно маєте взяти на себе зобов’язання, тому спочатку додайте зміни, які ви тільки що вибрали, а потім виконайте git commit --amend.

Це воно. Ви можете натиснути цю об'єднану версію у відділенні "об'єднані-коміти", якщо хочете.

Крім того, зараз ви можете відмовитись від двох повернень у вашій головній гілці. Просто оновіть свою головну галузь:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull

0

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

Я вважаю:

  • Ви використовуєте vi в якості свого редактора
  • Ваші зобов’язання є однорядними

Я тестував с git version 2.14.3 (Apple Git-98).


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact

Незрозуміло, що робить ваш сценарій.
buhtz

@buhtz Я додав ще кілька коментарів. Повідомте мене, якщо ви все-таки вважаєте це заплутаним, і якщо так, то яка частина.
erwaman

Досі незрозуміло, чим займається ваша робота. Також expectє невиписаним.
buhtz

@buhtz, яка частина незрозуміла? Я надав посилання на сторінку з додатковою документацією для expect.
erwaman

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