Git - раздавить всю ветку - однострочная команда сквоша

Пока я работаю над новым кодом, я делаю много небольших коммитов, чтобы отслеживать свои изменения. Моя компания, однако, предпочитает, чтобы каждая функция фиксировалась как одна фиксация. Итак, решение состоит в том, чтобы сжать всю мою (локальную) ветку до одного коммита.

Как мне раздавить всю ветку без использования git rebase --interactive, а затем изменить pick на squash для всех коммитов?


person masstroy    schedule 29.12.2014    source источник
comment
Может быть, использовать git merge --squash, чтобы не раздавить всю ветку перед рукой?   -  person twalberg    schedule 29.12.2014
comment
@twalberg, пожалуйста, ответьте   -  person jthill    schedule 30.12.2014
comment
git merge --squash не работает, если вы используете запросы на вытягивание Github для большей части слияния веток, что является довольно распространенным случаем.   -  person meagar    schedule 30.12.2014
comment
@meagar Даже тогда его можно заставить работать, приложив немного дополнительных усилий. А именно вторая ветка специально для интеграции. Вы выполняете всю свою работу в своей dev ветке, затем делаете git --merge squash в своей integration ветке и используете integration в качестве источника для вашего запроса на перенос.   -  person twalberg    schedule 30.12.2014
comment
@twalberg Это не лишние усилия, это уродливые накладные расходы.   -  person meagar    schedule 30.12.2014
comment
Лучшее, что я могу придумать, - это git reset --soft, за которым следует git commit.   -  person Alexey    schedule 19.03.2015
comment
(И, возможно, за ним следует git diff, чтобы проверить результат.)   -  person Alexey    schedule 19.03.2015


Ответы (7)


Я предпочитаю двухстрочный метод (исключая шаги 1 и 4 ниже). Преимущества заключаются в том, что вам не нужно знать / записывать какие-либо идентификаторы фиксации, вы можете написать простой псевдоним для выполнения всех задействованных шагов и фактически переместить всю свою ветку в origin / master, чтобы фактический слияние с мастером может быть быстрой перемоткой вперед, и не может быть никаких конфликтов.

Во-первых, мои предположения:

  • Вы работаете над веткой под названием my-feature-branch. Эта ветвь отошла от master на несколько коммитов; это проверенная ветка.
  • Ваш локальный master отслеживает удаленную ветку origin/master
  • Вы хотите сжать все свои коммиты из my-feature-branch в одну фиксацию поверх текущего состояния origin / master (а не вашего локального master, который может быть устаревшим)
  • Все ваши изменения зафиксированы, у вас нет неустановленных изменений (они будут потеряны в течение git reset --hard)

Мой процесс выглядит следующим образом:

  1. Получить, поэтому origin/master является текущим:

    $ git fetch
    
  2. Выбросьте все коммиты в вашей локальной ветке, установив для нее значение origin/master

    $ git reset --mixed origin/master
    
  3. Объедините все ваши старые изменения из предыдущего состояния вашей ветки в индекс

    $ git merge --squash HEAD@{1}
    
  4. Зафиксируйте свои изменения - Git предварительно заполнит ваш редактор сообщением фиксации, содержащим все сообщения фиксации из сжатых коммитов.

Я упомянул простой псевдоним:

alias squash="git fetch; git reset --mixed origin/master; git merge --squash HEAD@{1}"
person meagar    schedule 29.12.2014
comment
почему git reset --mixed вместо --hard? Смешанный сброс в моем случае сохранил все извлеченные файлы в рабочей копии, и это не удалось на этапе слияния. - person Mariano Desanze; 08.08.2019

Вероятно, лучшим вариантом для этого было бы использование git merge --squash во время слияния. Это оставит вашу ветку в том виде, в каком она была разработана, что довольно часто намного проще устранять неполадки, потому что у вас будет некоторое представление «Я изменял эту конкретную функцию в фиксации Z», и, глядя на эту конкретную фиксацию, у вас есть все контекст любых изменений, которые вы внесли в несколько файлов - глядя на одну фиксацию, которая представляет собой сжатые результаты вашего пути разработки, становится немного сложнее запомнить «О, да, мне пришлось изменить еще одну вещь в другом файле , тоже...". У вас также есть преимущество использования git bisect, когда у вас есть весь ваш путь - все, что он может сказать вам в случае сжатого, это «этот огромный коммит здесь что-то сломал».

Результатом использования git merge --squash является один коммит ветки, которую вы «объединяете», которая содержит кумулятивные изменения из вашей ветки, но оставляет исходную ветку в покое.

person twalberg    schedule 29.12.2014

Это идеальный вариант использования git reset --soft.

Предположим, у вас есть история коммитов

D   Your latest patch
C   Your second patch
B   Your first patch
A   Someone else's work

у вас нет поэтапных изменений, и git status, git log или git show говорят, что вы сейчас находитесь на фиксации D.

Затем git reset --soft B возьмет совокупные изменения коммитов C и D и подготовит их для фиксации. git commit --amend затем «объединит» эти изменения в коммит B.

Используйте следующим образом:

git reset --soft B
git commit --amend

Вторая команда вызовет ваш редактор с возможностью отредактировать сообщение фиксации.

Обратите внимание: если вы внесли поэтапные изменения перед запуском этого процесса (т.е. вы выполнили git add XXX, но не добавили git commit), то эти поэтапные изменения также будут объединены в фиксацию.

person ianinini    schedule 25.09.2019
comment
Я думаю, что это действительно недооцененный ответ, и он полезен не только для слияния. - person Rob Cannon; 18.04.2020
comment
Хммм, если вы сделаете это, вы больше не сможете тянуть или нажимать на свой пульт. Вы получите сообщение об ошибке fatal: refusing to merge unrelated histories - person Chris Moschini; 07.10.2020
comment
@ChrisMoschini, эта ошибка может возникнуть только в том случае, если пользователь манипулирует патчем, который уже был загружен. ОП спрашивал о манипулировании его локальными патчами (теми, которые не были запущены). В моем примере патчи B, C, D - это только локальные патчи. Патч A представляет ГОЛОВУ ветки на сервере. Патчи B, C и D преобразуются в один патч. - person ianinini; 08.10.2020

Найдите хэш для фиксации непосредственно перед запуском ветки и скопируйте его в буфер обмена. Затем выполните сброс к этому хешу.

$ git reset [hash]

Затем просто повторно добавьте и повторно зафиксируйте изменения в одном сообщении.

$ git add -A
$ git commit -m 'EVERYTHING SQUASHED'
person onlythefinestwilldo    schedule 29.12.2014
comment
Если вы хотите выполнить мягкий сброс, вам нужно явно использовать флаг --soft (git reset --soft <commit>), потому что Git по умолчанию выполняет смешанный сброс. - person jub0bs; 30.12.2014
comment
Но смешанный сброс по умолчанию обеспечивает желаемое сжатие. - person onlythefinestwilldo; 30.12.2014
comment
Да, но вы пишете: выполните мягкий сброс этого хэша; это заблуждение. - person jub0bs; 30.12.2014

Отредактируйте файл конфигурации git ~/.gitconfig и добавьте следующее в раздел псевдонимов

[alias]
    squash = "!f(){ CUR=`git rev-parse HEAD` && git reset --soft ${1} && git commit -m \"$(git log --format=%B ${1}..${CUR})\"; };f"

Этот псевдоним получает текущий хэш фиксации HEAD, сбрасывает его обратно на указанную вами фиксацию и создает новую фиксацию, сохраняющую все сообщения фиксации.

Использование:

git squash <refspec>

refspec может быть любой допустимой ссылкой на фиксацию, например хешем фиксации, именем ветки, именем тега, HEAD^ HEAD~3

person masstroy    schedule 29.12.2014

Я не думаю, что это правильный ответ на этот вопрос.

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

git rebase -i master

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

person Yeo    schedule 08.11.2015

Лучше всего было бы сделать полный сброс и объединить предыдущую HEAD со сквошем. Вот псевдоним:

[alias]
  squash = "!f() { git reset --hard $1; git merge --squash HEAD@{1}; git commit; }; f"

Вы можете назвать это так:

git squash master

Или сквошить из другой ветки, например dev:

git squash dev
person Giovanni Bassi    schedule 15.01.2019