Сквош мой последний X коммитов вместе с помощью Git

Как я могу сжать мои последние X коммитов в один коммит с помощью Git?

48 ответов

Решение

Использование git rebase -i <after-this-commit> и замените "pick" на втором и последующих коммитах на "squash" или "fixup", как описано в руководстве.

В этом примере <after-this-commit> является либо хэшем SHA1, либо относительным местоположением из HEAD текущей ветви, из которой анализируются коммиты для команды rebase. Например, если пользователь желает просмотреть 5 коммитов из текущего HEAD в прошлом, команда git rebase -i HEAD~5,

Вы можете сделать это довольно легко без git rebase или же git merge --squash, В этом примере мы раздавим последние 3 коммита.

Если вы хотите написать новое сообщение с нуля, этого достаточно:

git reset --soft HEAD~3 &&
git commit

Если вы хотите начать редактирование нового сообщения о коммите с конкатенацией существующих сообщений о коммите (т. Е. Аналогично пику /squash/squash/…/squash git rebase -i список команд будет начинаться с вас), затем вам нужно извлечь эти сообщения и передать их git commit:

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Оба эти метода объединяют последние три коммита в один новый коммит таким же образом. Мягкий сброс просто перенаправляет HEAD на последний коммит, который вы не хотите раздавливать. Программный сброс не затрагивает ни индекс, ни рабочее дерево, оставляя индекс в желаемом состоянии для вашего нового коммита (т. Е. Он уже содержит все изменения из коммитов, которые вы собираетесь "выбросить").

Ты можешь использовать git merge --squash для этого, что немного более элегантно, чем git rebase -i, Предположим, что вы мастер, и вы хотите раздавить последние 12 коммитов в один.

ВНИМАНИЕ: Сначала убедитесь, что вы совершаете свою работу - проверьте, что git status чисто (так как git reset --hard выбросит поэтапные и не постановочные изменения)

Затем:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

Документация дляgit merge описывает --squash вариант более подробно.


Обновление: единственное реальное преимущество этого метода перед более простым git reset --soft HEAD~12 && git commit Крис Джонсен в своем ответе предположил, что вы получаете сообщение о коммите, предварительно заполненное каждым сообщением о коммите, которое вы нажимаете.

2020 Простое решение без ребаза:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2 означает, что последние два коммита будут сжаты. Вы можете заменить его на любой номер

Я рекомендую избегать git reset когда это возможно - особенно для Git-новичков. Если вам действительно не нужно автоматизировать процесс, основанный на ряде коммитов, есть менее экзотический способ...

  1. Поместите коммиты, которые будут сдавлены, в рабочую ветку (если они еще не сделаны) - используйте для этого gitk
  2. Проверьте целевую ветвь (например, "мастер")
  3. git merge --squash (working branch name)
  4. git commit

Сообщение фиксации будет предварительно заполнено на основе сквоша.

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

git rebase -i HEAD~3

Это удобно, так как работает, даже когда вы находитесь в локальной ветке без информации об отслеживании / удаленного репо.

Команда откроет интерактивный редактор ребаз, который затем позволит вам изменить порядок, сквош, перефразировать слова и т. Д. Как обычно.


Использование интерактивного редактора ребаз:

Интерактивный редактор rebase показывает последние три коммита. Это ограничение было определено HEAD~3 при запуске команды git rebase -i HEAD~3,

Самый последний коммит, HEAD, отображается первым в строке 1. Строки, начинающиеся с # Есть комментарии / документация.

Документация отображается довольно ясно. В любой строке вы можете изменить команду с pick на команду по вашему выбору.

Я предпочитаю использовать команду fixup так как это "сдавливает" изменения коммита в коммит в строке выше и отбрасывает сообщение коммита.

Поскольку коммит в строке 1 HEADв большинстве случаев вы бы оставили это как pick, Вы не можете использовать squash или же fixup поскольку нет другого коммита, чтобы раздавить коммит в.

интерактивный редактор ребаз

Основываясь на ответе Криса Джонсена,

Добавьте глобальный псевдоним "squash" из bash: (или Git Bash в Windows)

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

... или с помощью командной строки Windows:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


Ваш ~/.gitconfig теперь должен содержать этот псевдоним:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


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

git squash N

... который автоматически сдавливает последний N совершает включительно.

Примечание. Результирующее сообщение о фиксации представляет собой комбинацию всех сжатых фиксаций по порядку. Если вы недовольны этим, вы всегда можете git commit --amend изменить его вручную. (Или измените псевдоним в соответствии со своими вкусами.)

В ветке, в которой вы хотите объединить коммиты, запустите:

git rebase -i HEAD~(n number of commits back to review)

Это откроет текстовый редактор, и вы должны переключить "pick" перед каждым коммитом с помощью "squash", если вы хотите, чтобы эти коммиты были объединены вместе. Например, если вы хотите объединить все коммиты в один, "пикап" - это первый коммит, который вы сделали, и все будущие (расположенные ниже первого) должны быть установлены на "сквош". Если используется vim, используйте :x в режиме вставки для сохранения и выхода из редактора.

Затем, чтобы закончить ребаз:

git rebase --continue

Подробнее об этом и других способах переписывания истории коммитов читайте в этом полезном посте.

Для этого вы можете использовать следующую команду git.

 git rebase -i HEAD~n

n (= 4 здесь) - номер последнего коммита. Тогда у вас есть следующие варианты,

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

Обновление как ниже,

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

Для получения подробной информации нажмите на ссылку

Удачи!!

Вот наглядный пример того, что будет после выполнения: git rebase -i HEAD~3

Многие ответы основаны на git rebase команда, но по моему опыту она несколько сложна и продвинута для начинающих git.

Допустим, вы хотите отменить последние 3 коммита. Затем следующие шаги:

  • Сначала проверьте журнал git : используйте git log -1 --oneline чтобы записать идентификатор фиксации текущего состояния (на случай, если вы сделаете что-то не так с git reset)
  • Вернуться на 3 коммита : Использование git reset --soft HEAD~3 вы вернетесь на 3 коммита (и как бы забудете, что вы сделали эти три коммита ранее)
  • Сделайте новую фиксацию : теперь просто сделайте git commit -m <NEW_SINGLE_MESSAGE> который автоматически объединит три коммита под вашим сообщением

Если что-то пойдет не так с git reset, вы можете снова вернуться в исходное состояние, git reset --soft <ORIGINAL_COMMIT>

Если вы используете TortoiseGit, вы можете функцию Combine to one commit:

  1. Открыть контекстное меню TortoiseGit
  2. Выбрать Show Log
  3. Отметьте соответствующие коммиты в представлении журнала
  4. Выбрать Combine to one commit из контекстного меню

Объединить коммиты

Эта функция автоматически выполняет все необходимые одиночные шаги git. К сожалению, доступно только для Windows.

Основываясь на этой статье, я нашел этот метод проще для моего сценария использования.

Моя ветка 'dev' опередила 'origin/dev' на 96 коммитов (поэтому эти коммиты еще не были перенесены на удаленный).

Я хотел раздавить эти коммиты в один, прежде чем приступить к изменениям. Я предпочитаю сбросить ветку в состояние origin / dev (это оставит все изменения из 96 коммитов без изменений), а затем зафиксировать изменения сразу:

git reset origin/dev
git add --all
git commit -m 'my commit message'

Ответ на вопрос об аномиях хорош, но я чувствовал себя неуверенно по этому поводу, поэтому решил добавить пару скриншотов.

Шаг 0: Git Log

Посмотри, где ты с git log, Самое главное, найти хеш коммита первого коммита, который вы не хотите раздавить. Так что только

введите описание изображения здесь

Шаг 1: git rebase

казнить git rebase -i [your hash], в моем случае:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Шаг 2: выбрать / раздавить, что вы хотите

В моем случае я хочу раздавить все на коммите, который был первым во времени. Порядок от первого до последнего, так же, как и в git log, В моем случае я хочу:

введите описание изображения здесь

Шаг 3: Настройте сообщение (я)

Если вы выбрали только один коммит и раздавили остальные, вы можете настроить одно сообщение о коммите:

введите описание изображения здесь

Вот и все. Как только вы сохраните это (:wq), все готово. Посмотрите на это с git log,

Я думаю, что самый простой способ сделать это - создать новую ветку на основе master и выполнить слияние - сквош функциональной ветви.

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

Тогда у вас есть все изменения, готовые к фиксации.

1) Определить коммит с коротким хешем

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

Здесь даже git log --oneline также может быть использован для получения короткого хэша.

2) Если вы хотите раздавить (объединить) последние два коммита

# git rebase -i deab3412 

3) Это открывает nano редактор для слияния. И это выглядит ниже

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4) Переименуй слово pick в squash который присутствует раньше abcd1234, После переименования это должно быть как ниже.

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5) Теперь сохраните и закройте nano редактор. Нажмите ctrl + o и нажмите Enter сохранить. А затем нажмите ctrl + x выйти из редактора.

6) Тогда nano редактор снова открывается для обновления комментариев, при необходимости обновите его.

7) Теперь он успешно сдавлен, вы можете проверить это, проверив логи.

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8) Теперь нажмите на репо. Примечание для добавления + подписать перед названием филиала. Это означает принудительный толчок.

# git push origin +master

Примечание: это основано на использовании git на ubuntu ракушка. Если вы используете разные ОС (Windows или же Mac) тогда приведенные выше команды такие же, кроме редактора. Вы можете получить другой редактор.

Чтобы раздавить последние 10 коммитов в один коммит:

git reset --soft HEAD~10 && git commit -m "squashed commit"

Если вы также хотите обновить удаленную ветку с помощью сжатого коммита:

git push -f

Например , если вы хотите объединить все коммиты в один коммит в ветке (удаленное хранилище), например: https://bitbucket.org/

Что я сделал

  1. git reset --soft Head~3 &&
  2. мерзавец совершить
  3. git push origin --force

Если вы находитесь в удаленном филиале (называется feature-branch) клонирован из Золотого Хранилища (golden_repo_name), то вот методика раздавливания ваших коммитов в один:

  1. Оформить заказ на золотой репо

    git checkout golden_repo_name
    
  2. Создайте из него новую ветку (золотой репо) следующим образом

    git checkout -b dev-branch
    
  3. Сквош слиться с местным филиалом, который у вас уже есть

    git merge --squash feature-branch
    
  4. Зафиксируйте ваши изменения (это будет единственный коммит в ветке dev)

    git commit -m "My feature complete"
    
  5. Вставьте ветку в ваш локальный репозиторий

    git push origin dev-branch
    

Что может быть действительно удобным:
Найдите хеш коммита, который вы хотите использовать, скажем d43e15,

Сейчас использую

git reset d43e15
git commit -am 'new commit name'

Кто-нибудь упоминал, как легко это сделать в пользовательском интерфейсе IntelliJ IDEA:

  • Перейти к git окно
  • Выберите все коммиты, которые вы хотите объединить в один.
  • Щелкните правой кнопкой мыши> Squash > Отредактируйте сообщение фиксации
  • Щелкните имя ветки слева> щелкните правой кнопкой мыши> Нажать> Push Force

метод 1 , если у вас много коммитов

git rebase -i masterзатем нажмите клавиатуру «i», чтобы отредактировать

вы увидите следующее:

      pick etc1
pick etc2
pick etc2

заменить слово выбрать на 'f'и нажмите esc y :wq

      pick etc1 //this commit will the one commit
f etc2
f etc2

и нажмите эту команду

      git push origin +head

метод 2 , если у вас мало коммитов, вы можете сделать это, чтобы удалить коммит, вам нужно сделать то же самое для удаления второго коммита и т. д.

      git reset --soft HEAD^1
git commit --amend
git push -f

метод 3 , если у вас уже есть одна фиксация и вы не хотите отправлять еще одну фиксацию

      git add files...
git commit --amend  //then press `:wq`
git push origin +head

простое решение:

git reset --soft HEAD~5

git commit -m "commit message"

git push origin branch --force-with-lease

Если вы хотите объединить каждый коммит в один коммит (например, при первом публичном выпуске проекта), попробуйте:

git checkout --orphan <new-branch>
git commit

Это очень круто, но довольно круто, так что я просто брошу это на ринг:

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

Перевод: предоставьте новый "редактор" для git, который, если имя файла для редактирования git-rebase-todo (интерактивная подсказка перебазирования) изменяет все, кроме первого "пика", на "сквош", а в противном случае порождает vim - так что, когда вам будет предложено отредактировать сжатое сообщение о коммите, вы получите vim. (И, очевидно, я давил последние пять коммитов на ветке foo, но вы можете изменить это так, как вам нравится.)

Я бы, наверное, сделал то, что предложил Марк Лонгэйр.

Самый простой способ сделать это — использовать GitHub Desktop. Просто выберите все коммиты в вашей истории, щелкните правой кнопкой мыши и выберите «Squash x commits»:

Простой однострочный, который всегда работает, учитывая, что вы в данный момент находитесь в ветви, которую хотите сжать, master - это ветвь, из которой он возник, а последний коммит содержит сообщение о коммите и автора, которого вы хотите использовать:

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}

⚠️ ВНИМАНИЕ: "Мои последние X коммиты" могут быть неоднозначными.

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

В этой очень сокращенной истории хранилища https://github.com/fleetwood-mac вы открыли пулл-запрос для слияния коммита Билла Клинтона с оригиналом (MASTER) Fleetwoodd Mac коммит.

Вы открыли пулл-запрос и на GitHub вы видите это:

Четыре коммитов:

  • Добавить Дэнни Кирван
  • Добавить Кристина Идеал
  • LA1974
  • Билл Клинтон

Думая, что никто не захочет читать всю историю хранилища. (Там на самом деле есть хранилище, нажмите на ссылку выше!) Вы решили уничтожить эти коммиты. Так ты иди и беги git reset --soft HEAD~4 && git commit, Затем вы git push --force это на GitHub, чтобы очистить ваш PR.

А что происходит? Вы только что сделали один коммит от Фрица до Билла Клинтона. Потому что вы забыли, что вчера вы работали над версией Бекингемского Ника. А также git log не соответствует тому, что вы видите на GitHub.

МОРАЛЬ ИСТОРИИ

  1. Найдите нужные вам файлы и git checkout их
  2. Найдите точный предыдущий коммит, который вы хотите сохранить в истории, и git reset --soft тот
  3. Делать git commit который искажает непосредственно от к к

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

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend

Как с помощью Git объединить последние коммиты X в один?

git rebase -i HEAD~X

Будет показан следующий контент:

pick 1bffc15c My earlier commit
pick 474bf0c2 My recent commit

# ...

Для коммитов, которые вы хотите сжать, замените pick на fixup, чтобы он стал:

pick 1bffc15c My earlier commit
fixup 474bf0c2 My recent commit

# ...

Если он открыт в vim (интерфейс по умолчанию в терминале), нажмите Esc на клавиатуре введите :wq а также Enter чтобы сохранить файл.

Проверить: Проверитьgit log

Другие вопросы по тегам