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

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

29 ответов


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

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


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

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

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

если вы хотите начать редактирование нового сообщения фиксации с конкатенацией существующих сообщений фиксации (т. е. аналогично тому, что pick/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})"

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


можно использовать git merge --squash для этого, что немного более элегантно, чем git rebase -i. Предположим, вы на master и хотите раздавить последние 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 описание в более деталь.


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


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

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

сообщение фиксации будет на основе тыквы.


на основе ответ Криса Джонсена,

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

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

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

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


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

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


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

git squash N

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

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


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

git rebase -i HEAD~3

это удобно, так как он работает, даже если вы находитесь в локальной ветке без отслеживания информации/удаленного РЕПО.

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


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

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

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

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

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

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

interactive rebase editor


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

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

Combine commits

эта функция автоматически выполняет все необходимые действия один мерзавец. К сожалению, доступно только для Windows.


на основе в этой статье Я нашел этот метод проще для моего usecase.

моя ветвь " dev "опережала" origin/dev " на 96 коммитов (поэтому эти коммиты еще не были нажаты на пульт).

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

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

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 to 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 on ubuntu ракушка. Если вы используете другую ОС (Windows или Mac), то выше команды такие же, кроме редактора. Вы можете получить другой редактор.


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

 git rebase -i HEAD~n

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

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

обновление, как bellow,

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

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

удачи!!


если вы находитесь в удаленной ветке(называется 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-branch)

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

    git push origin dev-branch
    

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

Шаг 0: git log

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

enter image description here

Шаг 1: git rebase

выполнить git rebase -i [your hash] в моей дело:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

Шаг 2: Выберите / сквош, что вы хотите

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

enter image description here

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

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

enter image description here

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


это супер-пупер kludgy, но в некотором роде круто, поэтому я просто брошу его в кольцо:

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

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

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


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

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

что может быть очень удобно: Найдите хэш фиксации, который вы хотите раздавить поверх; скажите, что это d43e15 Теперь используйте

git reset d43e15

git commit -am 'new commit name'


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

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

тогда у вас есть все изменения готовы совершить.


Я нахожу более общее решение-не указывать " N " коммитов, а скорее ветвь/commit-id, которую вы хотите раздавить поверх. Это менее подвержено ошибкам, чем подсчет коммитов до определенного коммита-просто укажите тег напрямую, или если вы действительно хотите подсчитать, вы можете указать HEAD~N.

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

Я использую псевдоним:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\"; sed -i .tmp '2,\$s/^pick/f/' \"\\"; }; _\"" git rebase -i

это сбросит историю, раздавленную до этого-это дает вам возможность восстановить, схватив старый идентификатор фиксации с консоли, если вы хотите вернуться. (Пользователи Solaris отмечают, что он использует GNU sed -i опция, пользователи Mac и Linux должны быть в порядке с этим.)


в вопросе может быть неоднозначно, что подразумевается под "последним".

git log --graph выводит следующие (упрощенный):
* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

тогда последние коммиты по времени - H0, merge, B0. Чтобы раздавить их, вам придется перебазировать объединенную ветвь на commit H1.

проблема в том, что H0 содержит H1 и H2 (и, как правило, больше коммитов до слияния и после ветвления), а B0-нет. Таким образом, вы должны управлять изменениями из H0, merge, H1, H2, B0 в наименьший.

можно использовать rebase, но по-разному, а затем в других упомянутых ответах:

rebase -i HEAD~2

это покажет вам варианты выбора (как уже упоминалось в других ответов):

pick B1
pick B0
pick H0

положите сквош вместо выбора в H0:

pick B1
pick B0
s H0

после сохранения и выхода rebase будет применять фиксации по очереди после H1. Это означает, что он попросит вас снова разрешить конфликты (где HEAD сначала будет H1, а затем накапливает коммиты как они применяются).

после завершения rebase вы можете выбрать сообщение для раздавленных H0 и B0:

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

P. S. Если вы просто сделать некоторые сброса БО: (например, используя reset --mixed это объясняется более подробно здесь https://stackoverflow.com/a/18690845/2405850):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

затем вы сквош в B0 изменения H0, H1, H2 (потеря полностью фиксирует изменения после ветвления и до слияния.


git rebase -i HEAD^^

где число ^ ' s равно X

(в этом случае раздавите два последних коммита)


взгляните на эту суть:

Gist-Easy git-сквош

вам нужно будет ввести, например git-squash 3 и это все. Последние три коммита слились в одно, а их сообщения были объединены.


В дополнение к другим отличным ответам, я хотел бы добавить, как git rebase -i всегда путает меня с порядком фиксации-старше к более новому или наоборот? Итак, это мой рабочий процесс:

  1. git rebase -i HEAD~[N], где N-количество коммитов, к которым я хочу присоединиться,начиная с последней. Так что git rebase -i HEAD~5 будет означать "раздавить последние 5 коммитов в новый";
  2. появляется редактор, показывающий список коммитов, которые я хочу объединить. Теперь они отображаются в обратный порядок: старшая фиксация сверху. Отметьте как "сквош" или " s " все коммиты там кроме первого/старшего: он будет использоваться в качестве отправной точки. Сохранить и закрыть редактор;
  3. редактор снова появляется с сообщением по умолчанию для новой фиксации: измените его на свои потребности, сохраните и закройте. Сквош завершен!

источники и дополнительные гласит: #1, #2.


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

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

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

git push -f

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

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

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

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

git rebase --continue

подробнее об этом и других способах переписать историю фиксации см. В разделе этот полезный пост


как насчет ответа на вопрос, связанный с такой документооборот?

  1. многие локальные коммиты, смешивается с несколькими слияниями от master,
  2. наконец толчок к удаленному,
  3. PR и слияние в master Юра. (Да, разработчику было бы проще merge --squash после PR, но команда думала, что это замедлит процесс.)

Я не видел такого рабочего процесса на эта страница. (Это могут быть мои глаза.) Если я понимаю rebase правильно, для нескольких слияний потребуется несколько вариантов разрешения конфликта. Я не хочу даже думать об этом!

так, это, кажется, работает для нас.

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. редактировать и совершать много локально, слияние master регулярно
  5. git checkout new-branch
  6. git merge --squash new-branch-temp // добавляет все изменения в сцена
  7. git commit 'one message to rule them all'
  8. git push
  9. рецензент делает PR и сливается с master.

Если вы используете GitUp, выберите фиксацию, которую вы хотите объединить с ее родителем, и нажмите S. Вы должны сделать это один раз для каждого коммита, но это намного проще, чем придумать правильное заклинание командной строки. Особенно, если это то, что ты делаешь только время от времени.


просто добавьте эту функцию bash в свой bash .файл zshrc.

# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
    if [ -z "" -o -z "" ]; then
        echo "Usage: \`squash X COMMIT_MSG\`"
        echo "X= Number of last commits."
        echo "COMMIT_MSG= New commit msg."
        return 1
    fi

    git reset --soft HEAD~""
    git add . && git ci -m "" # With 100 emoji
    git push --force
}

тогда просто запустите

squash X 'New Commit Message'

и вы сделали.


другой способ сделать это, если у вас есть тонна коммитов, - это сделать сквош после фиксации, такой как git rebase -i <hashbeforeyouwanttosquash>

Это откроет ваш редактор, чтобы выбрать / сквош, как обычно.

см.https://git-scm.com/docs/git-rebase#_interactive_mode


сначала я узнаю количество коммитов между моей веткой функции и текущей главной веткой по

git checkout master
git rev-list master.. --count

затем я создаю другую ветвь на основе ветви my-feature, keep my-feature филиала нетронутой.

наконец, я бегу

git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge

надеюсь, это поможет, спасибо.


переключитесь на главную ветку и убедитесь, что вы в курсе:

sh git checkout master && git fetch && git pull

объединить вашу ветку в ветку master на месте:

sh git merge feature_branch

сброс локальной главной ветви в состояние origin:

sh git reset origin/master

теперь все ваши изменения считаются неустановленными. Вы можете организовать и зафиксировать их в одном или нескольких фиксациях.

sh git add . --all git commit