Как сжать коммиты в git после того, как они были отправлены?

Это дает хорошее объяснение раздавливания нескольких коммитов:

http://git-scm.com/book/en/Git-Branching-Rebasing

но это не работает для уже отправленных коммитов. Как мне раздавить несколько последних коммитов как в локальном, так и в удаленном репозиториях?

Когда я делаю git rebase -i origin/master~4 master, оставляю первый как pick, устанавливаю остальные три как squash, а затем выхожу (через c-x c-c в emacs), я получаю:

$ git rebase -i origin/master~4 master
# Not currently on any branch.
nothing to commit (working directory clean)

Could not apply 2f40e2c... Revert "issue 4427: bpf device permission change option added"
$ git rebase -i origin/master~4 master
Interactive rebase already started

где 2f40 - это pick фиксация. И теперь ни один из 4 коммитов не появляется в git log. Я ожидал, что мой редактор будет перезапущен, чтобы я мог ввести сообщение о фиксации. Что я делаю неправильно?


person Loren    schedule 14.04.2011    source источник
comment
Не так исчерпывающе, но легко читается: internalpointers.com/post/squash -commits-into-one-git   -  person Janos Vinceller    schedule 03.01.2021


Ответы (8)


Squash фиксируется локально с помощью

git rebase -i origin/master~4 master

а затем принудительно нажать с помощью

git push origin +master

Разница между --force и +

Из документации git push:

Обратите внимание, что --force применяется ко всем отправляемым ссылкам, поэтому его использование с push.default, установленным в matching, или с несколькими назначениями push, настроенными с помощью remote.*.push, может перезаписать ссылки, отличные от текущей ветки (включая локальные ссылки, которые строго отстают от своего удаленного аналога). Чтобы принудительно передать только одну ветку, используйте + перед refspec для push (например, git push origin +master, чтобы принудительно отправить в ветку master).

person Alan Haggai Alavi    schedule 14.04.2011
comment
вы также можете git push --force origin master - person Daenyth; 14.04.2011
comment
Daenyth: Да, но я всегда предпочитаю этот синтаксис, поскольку он короче. - person Alan Haggai Alavi; 14.04.2011
comment
И, конечно же, имейте в виду, что если кто-то еще мог извлечь данные из удаленного репозитория, вы, вероятно, не захотите этого делать - в этом случае ответ - нет. - person Cascabel; 14.04.2011
comment
Кроме того, я думаю, что OP точно копирует команду git rebase -i origin/master и на самом деле хочет знать, как перебазировать коммиты дальше, чем это, например. git rebase -i origin/master~20 master. - person Cascabel; 14.04.2011
comment
@Jefromi: Почему? Что будет в таком случае? Пожалуйста, объясните кому-то вроде меня, кто не пробовал это делать. - person HelloGoodbye; 07.08.2013
comment
@HelloGoodbye Потому что тогда их история будет конфликтовать с переписанной историей, которую вы только что выдвинули, и разобраться со всеми может быть огромной болью. См., Например, kernel.org/pub/software / scm / git / docs /, или один из множества вопросов по этому поводу. - person Cascabel; 07.08.2013
comment
@Jefromi: Но если вы уверены, что никто не извлекал данные из удаленного репозитория, вы в порядке? Например, если вы единственный человек, имеющий доступ для чтения к удаленному репозиторию, и вы не использовали его в любом другом локальном репозитории, который у вас может быть. - person HelloGoodbye; 07.08.2013
comment
@HelloGoodbye Да, в моем комментарии действительно говорилось , если кто-то другой мог получить информацию из удаленного репозитория. - person Cascabel; 07.08.2013
comment
Для общедоступной службы git вполне вероятно, что вы столкнулись с denying non-fast-forward, см. Заголовок stackoverflow.com/questions/22026489/ для решения - person Ding-Yi Chen; 25.08.2014
comment
+ Равно --force? - person gstackoverflow; 22.11.2014
comment
gstackoverflow: + заставляет только refspec, которому он предшествует. --force принудительно отправит все refspecs. См. Обновленный ответ. - person Alan Haggai Alavi; 10.12.2014
comment
Как можно локально сквошировать коммиты после того, как их подтолкнули? Всякий раз, когда я пытаюсь это сделать, в редакторе просто не слышно. - person jonS90; 22.01.2015
comment
Думаю, я только что понял ... Мне нужно было вернуть несколько коммитов вверх. - person jonS90; 22.01.2015
comment
@Jefromi Вы правы, когда будете осторожны с перезаписью истории в удаленных ветках. Но бывают и случаи, когда это хорошая идея. Например, у меня есть несколько частных репозиториев для проектов с открытым исходным кодом. Я тестирую их на разных машинах, поэтому я все время отправляю частично завершенные ветки обратно на свой пульт. Когда приходит время сделать пул-реквест, я сжимаю все коммиты, что и создает эту ситуацию. И это предпочтительный метод участия в проектах с открытым исходным кодом. Но, как вы упомянули, главное знать, что никто другой не разветвляет мою частную ветку. - person Nilpo; 20.03.2016
comment
Это действительно работает так, как задумано? Я мог видеть git log, указывающий, что коммиты успешно сдавлены на моем локальном компьютере, но на другом клоне, скажем, на ПК другого разработчика (и, конечно, в Atlassian BitBucket Server), он перечисляет исходные несжатые коммиты, а затем сжатый коммит. Какой тогда смысл давить ?! Имеет ли значение версия git? (Надеюсь нет.) - person mystarrocks; 24.06.2016
comment
Хороший метод :) Вы также можете принудительно толкнуть (если действительно необходимо) с помощью git push --force-with-lease, чтобы сделать плавное усилие. Нашел здесь: developer.atlassian.com/blog/2015/04 / force-with-lease - person Benj; 28.06.2016
comment
Это не для меня. Делаю первую команду - git rebase -i origin / master ~ 20 master. Это дает мне окно vi, и я меняю все строки «выбора» на «сквош» и получаю сообщение об ошибке, которое невозможно «сквошить» без предыдущей фиксации. - person Steve Cohen; 07.09.2016
comment
@SteveCohen Вы должны оставить одну из коммитов как 'pick': rebase сдавливает 'сквоши' в фиксацию 'pick' (предположительно, первая 'pick' одна раньше, если вы оставите более одного как ' выбирать'). - person Stuart Rossiter; 02.11.2016
comment
Если вы возвращаетесь к корневому коммиту, вам нужно использовать git rebase -i --root, иначе он потерпит неудачу с fatal: Needed a single revision - person AndrewHarvey; 16.09.2018
comment
Примечание: вы должны изменить на squash все записи для коммитов, которые вы хотите сжать, и pick для тех, которые вы хотите выбрать. И коммиты появляются в обратном порядке (новые сверху). (См. stackoverflow.com/questions/39595034/) - person Michele Piccolini; 01.06.2020

В ветке я смог сделать это вот так (за последние 4 коммита)

git checkout my_branch
git reset --soft HEAD~4
git commit
git push --force origin my_branch
person jakob-r    schedule 16.09.2015
comment
Выполнение этого с помощью мягкой команды на уже продвинутой ветке привело к тому, что за меня подтолкнули тонны других людей. - person cchamberlain; 04.02.2016
comment
Тонна? Как может быть больше 4? Вы можете уточнить? - person jakob-r; 05.02.2016
comment
В этом я не уверен, но это как-то связано с попыткой сжать уже выдвинутый коммит. Похоже, что и другие здесь испытали подобное - stackoverflow.com/questions/5189560/ - person cchamberlain; 05.02.2016
comment
Я бы принял этот ответ, как и ожидалось. Более чистый, который принял ответ. - person vikramvi; 14.07.2016
comment
Это наиболее понятный и общепринятый ответ всего за 4 шага - person Ameya Salagre; 10.10.2018
comment
очень коротко и просто, вместо того, чтобы разрешать конфликты и создавать ветку с отдельной HEAD и всем остальным. - person amarnath harish; 07.05.2019
comment
это то, что я использую, потому что я застрял с интерактивным ребазингом, потому что после перемотки и изменения фиксации git скажет, что его текущая позиция находится в [‹someHash› | REBASE-i], и я не знаю, как продолжить, потому что ни одно из интернет-руководств, которые я просмотрел, не включает эту часть - person heug; 28.06.2019

Незначительное отличие от принятого ответа, но у меня были большие трудности с раздавливанием, и я, наконец, получил его.

$ git rebase -i HEAD~4
  • На открывшемся интерактивном экране замените pick на squash вверху для всех коммитов, которые вы хотите раздавить.
  • Сохраните и закройте редактор через esc --> :wq

Нажмите на пульт, используя:

$ git push origin branch-name --force
person BLRBoy    schedule 22.12.2017
comment
кратко и эффективно, подробно: - person terwxqian; 07.01.2019
comment
esc - ›: wq при условии, что редактором является vi или vim - person thanos.a; 22.01.2021

Многие проблемы можно избежать, если только создать branch, над которым нужно работать, а не работать над master:

git checkout -b mybranch

Следующее работает для remote уже отправленных коммитов и смеси remote нажатых коммитов / local только коммитов:

# example merging 4 commits

git checkout mybranch
git rebase -i mybranch~4 mybranch

# at the interactive screen
# choose fixup for commit: 2 / 3 / 4

git push -u origin +mybranch

У меня также есть несколько примечаний к запросу на вытягивание что может быть полезно.

person Stuart Cardall    schedule 05.09.2016

git rebase -i master

вы откроете редактор vm и сообщите что-то вроде этого

Pick 2994283490 commit msg1
f 7994283490 commit msg2
f 4654283490 commit msg3
f 5694283490 commit msg4
#Some message 
#
#some more

Здесь я изменил выбор для всех остальных коммитов на «f» (означает исправление).

git push -f origin feature/feature-branch-name-xyz

это исправит все коммиты для одного коммита и удалит все остальные коммиты. Я сделал это, и это мне помогло.

person Nupur    schedule 26.09.2017
comment
Это должен быть главный ответ, и спасибо. Я считаю, что этот ответ - лучший способ раздавить, чем делать это вслепую, основываясь на количестве коммитов. В частности, с редактором виртуальной машины вы можете делать много вещей одновременно, например изменять фиксацию, удалять фиксацию и, конечно же, раздавливать. - person MG Developer; 17.07.2020

1) git rebase -i HEAD~4

Для уточнения: он работает в текущей ветке; HEAD ~ 4 означает сжатие последних четырех коммитов; интерактивный режим (-i)

2) На этом этапе открывается редактор со списком коммитов, чтобы изменить второй и последующие коммиты, заменив pick на squash, а затем сохраните его.

вывод: Успешно перебазированы и обновлены ссылки / головки / имя-ветки.

3) git push origin refs/heads/branch-name --force

выход:

remote:
remote: To create a merge request for branch-name, visit:
remote: http://xxx/sc/server/merge_requests/new?merge_request%5Bsource_branch%5D=sss
remote:To ip:sc/server.git
 + 84b4b60...5045693 branch-name -> branch-name (forced update)
person terwxqian    schedule 07.01.2019

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

git rebase -i HEAD~2
    [ pick     older-commit  ]
    [ squash   newest-commit ]
git push --force

По умолчанию это будет включать сообщение о последней фиксации в качестве комментария к более старой фиксации.

person lobsterhands    schedule 23.10.2018

Когда вы работаете с Gitlab или Github, у вас могут возникнуть проблемы. Вы сдавливаете свои коммиты одним из описанных выше методов. Я предпочитаю:

git rebase -i HEAD~4
or
git rebase -i origin/master

выберите сквош или исправление для вашего коммита. На этом этапе вы должны проверить с помощью git status. И сообщение могло быть таким:

    On branch ABC-1916-remote
    Your branch and 'origin/ABC-1916' have diverged,
    and have 1 and 7 different commits each, respectively.
      (use "git pull" to merge the remote branch into yours)

И у вас может возникнуть соблазн потянуть его. НЕ ДЕЛАЙТЕ ЭТО, иначе вы окажетесь в той же ситуации, что и раньше.

Вместо этого перейдите к своему источнику с помощью:

git push origin +ABC-1916-remote:ABC-1916

+ Позволяет принудительно нажать только на одну ветку.

person Alex    schedule 13.08.2019
comment
Нет ничего плохого в вытягивании, особенно если у вас есть конфликты, их можно разрешить легче, чем если бы вы принудительно толкали - person Ray_Poly; 03.03.2020
comment
@Ray_Poly - разве это не вернет удаленные коммиты обратно как локальные? Вот что я нашел. - person Steve Dunn; 11.12.2020
comment
да, он создаст новый коммит, но вы можете увидеть в нем различия, лучше, чем переписывать историю git каждый раз, когда вы переустанавливаете - person Ray_Poly; 11.12.2020