Error cannot squash without a previous commit

I have the following in the to-do text of git rebase -i HEAD~2: pick 56bcce7 Closes #2774 pick e43ceba Lint.py: Replace deprecated link # Rebase 684f917..e43ceba onto 684f917 (2 command(s)) # ......

I had a similar problem which I solved as follows:

This is the commit group I wanted to squash:

1 s 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 pick 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

As you can see, I wanted no. 4, but 1, 2 and 3 had no previous commit to squash into. Hence the Cannot ‘squash’ without a previous commit error.

My solution was to use the r option for # r, reword = use commit, but edit the commit message

So my commits list looked like this:

1 r 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 s 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

After saving, the interactive shell asked me for the rewording of the chosen commit.

After that, my commit log resulted in a single commit which resulted in a cleaner commit history.

Git is one of the best software versioning and collaborative coding tools out there. That said, it isn’t exactly the easiest thing to pick up, especially considering the amount of complexity the tool offers when used in production environments. 

In this article, we’re talking about the “error: cannot squash without a previous commit” issue, its causes and how you can fix the problem.


What causes the problem?

The error can be triggered by a number of problems including:

  • Incorrect HEAD option.
  • Incorrect target commit
  • Improper logs in your Git commit history. 

Also read: How to create a Git repository? How to connect it to GitHub?


How to fix this?

Here are three fixes you can try out.

Restart your PC

This should be especially helpful if you’re on Windows. Rebooting your PC can fix all sorts of seemingly random issues with your programs and scripts as it often refreshes the entire operating system meaning corrupt caches and system memory are cleared. 


Use the correct HEAD option

When squasing a commit, be sure to use the appropriate HEAD option. Depends on when was your first commit, you might want to use a higher HEAD option or even –root. 

The general syntax to squash a commit using the HEAD option is as follows:

git rebase -i HEAD~X

X in the aforementioned command is the HEAD number your commit requires.


Clean up your Git logs

Another thing you can do is to clean up your Git logs. By doing so, you ensure that Git can see your commit history clearly and not missing anything imporatnt required to squash the required commit.

You can do this by using the -r option for reword. This uses the same commit, but edits the commit message which makes it easier for Git to differentiate between similar older commits. 

Also read: GitHub vs Git vs GitLab vs Bitbucket

Yadullah Abidi

Someone who writes/edits/shoots/hosts all things tech and when he’s not, streams himself racing virtual cars.

You can contact him here: [email protected]

I had a similar problem which I solved as follows:

This is the commit group I wanted to squash:

1 s 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 pick 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

As you can see, I wanted no. 4, but 1, 2 and 3 had no previous commit to squash into. Hence the Cannot ‘squash’ without a previous commit error.

My solution was to use the r option for # r, reword = use commit, but edit the commit message

So my commits list looked like this:

1 r 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 s 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

After saving, the interactive shell asked me for the rewording of the chosen commit.

After that, my commit log resulted in a single commit which resulted in a cleaner commit history.

I had this problem and the reason why it happened in my case was that, you cannot squash older commits onto a new commit. Here is an example say you have 3 commits:

1 pick 01mn9h78 The lastest commit
2 pick a2b6pcfr A commit before the latest
3 pick 093479uf An old commit i made a while back

Now if you say git rebase -i HEAD~3 and you do something like

1 pick 01mn9h78 The lastest commit
2 s a2b6pcfr A commit before the latest
3 s 093479uf An old commit i made a while back

This will result in the error:

error: cannot ‘squash’ without a previous commit
You can fix this with ‘git rebase —edit-todo’ and then run ‘git rebase —continue’.
Or you can abort the rebase with ‘git rebase —abort’.

Solution :

When squashing commits, you should squash recent commits to old ones not vice versa thus in the example it will be something like this:

1 s 01mn9h78 The lastest commit
2 s a2b6pcfr A commit before the latest
3 pick 093479uf An old commit i made a while back

This will work fine, incase you want all your commit messages, I would suggest fixup instead of squash.

Interactive rebase presents commits in the reverse order of what you are used to when using git log. git rebase -i replays the selected commits in the exact (top-down) order they are listed in the saved rebase instructions file. When squashing, the commit selected for squashing is combined with the commit that precedes it in the (edited) list, i.e. the commit from the previous line. In your case — there is no previous commit for 56bcce7. You have to do one of the following

  • git rebase -i HEAD~3 (if you want to squash 56bcce7 into 684f917)
  • If you mean to combine 56bcce7 with e43ceba, and e43ceba doesn’t depend on 56bcce7, then simply reorder them:

    r e43ceba Lint.py: Replace deprecated link
    s 56bcce7 Closes #2774
    

    UPDATE: Gus’s answer below suggests a better way of doing the same, without reordering the two commits:

    r 56bcce7 Closes #2774
    s e43ceba Lint.py: Replace deprecated link
    

    This will squash/merge the two commits into one. When the interactive rebase asks for a reworded commit message for 56bcce7, provide the commit message that describes the union of 56bcce7 and e43ceba.

Squash with the reverse logic. You’ll be able to select the desired commit message at the later step.

  • pick the first commit that you don’t want the commit-message for.
  • squash or fixup the commit(s) that you want to merge, until the one that has the commit message that you actually wanted.
pick 56bcce7 Closes #2774
squash e43ceba Lint.py: Replace deprecated link
  • confirm the change (:x)
  • delete the commit message(s) that you don’t want and leave only the message from the commit that you desire (in this case: Lint.py: Replace deprecated link).
  • confirm the choice (:x)

Hope it’s clearer for somebody ✌🏽

Contents

In this post, I would like to share how to squash commits using git-rebase.

Squash several commits with rebase

I have a git repo with the following commit history:

0022f04 - (HEAD -> master) demo 2 (2021-06-28 16:48:30 +0800) <jdhao>
e4072cf - demo 1 (2021-06-28 15:49:51 +0800) <jdhao>
da88b72 - line 3 (2021-06-28 14:47:43 +0800) <jdhao>
08b0518 - line 2 (2021-06-28 14:46:55 +0800) <jdhao>
ac0bd14 - line 1 (2021-06-28 14:46:33 +0800) <jdhao>

Since the most recent two commits are related, I want to squash them into one commit,
i.e., combining commit 0022f04 and e4072cf into one. How should I do it?

We can use git-rebase to achieve this. First, run the following command:

# rebase the recent two commits
git rebase -i HEAD~2

Here HEAD~2 refers to commit da88b72. So we are rebasing the most recent two commits against it.

After using this command, git will open the vim editor and show the following interface:

pick e4072cf demo 1
pick 0022f04 demo 2

# Rebase da88b72..0022f04 onto da88b72 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

The two commits we want to squash are shown inversely, compared to their position in git-log output.
In the interface, each line shows a specific commit. The format is <command> <commit-hash> <commit-msg>.

For squashing, a commit is squashed against previous commit, i.e., the line before it.
Note we can not squash first commit, since there is no previous line.
You will see errors if you try to do so:

error: cannot 'squash' without a previous commit
You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
Or you can abort the rebase with 'git rebase --abort'.

Okay, back to the rebase interface, we then edit this file and change pick in the lines after first line to squash.

pick e4072cf demo 1
squash 0022f04 demo 2

Then we use command :wq to save the edit and quit Vim.
Git will automatically open another interface —
this time for editing the commit message of the squashed commits.
It will pre-populate the file with commit messages of the squashed commits.
You can keep it as is or write a new and unified message.
Save the file and quit. Git will squash commit 0022f04 onto e4072cf, creating a new commit.
Now, git log shows the following message:

adc8ebd - (HEAD -> master) Demo 1 and 2 (2021-06-28 17:41:16 +0800) <jdhao>
da88b72 - line 3 (2021-06-28 14:47:43 +0800) <jdhao>
08b0518 - line 2 (2021-06-28 14:46:55 +0800) <jdhao>
ac0bd14 - line 1 (2021-06-28 14:46:33 +0800) <jdhao>

How to abort rebase when we are inside vim?

If we want to abort the rebase process when we are inside Vim, how do we do it?
Based on answer here, we should use command :cq to quit Vim.

Squash commits onto first commit

If we want to squash the first three commits into one, things become a little tricky,
since first commit is literally the root of all your commits, so there is no commit before it.
In this case, we need to use the --root option:

git rebase -i --root master

We have the following rebase interface:

pick ac0bd14 line 1
pick 08b0518 line 2
pick da88b72 line 3
pick adc8ebd Demo 1 and 2

# Rebase adc8ebd onto bcc1018 (4 commands)
#

We need to squash commit 08b0518 and da88b72 onto commit ac0bd14.
So we edit the file to become the following:

pick ac0bd14 line 1
squash 08b0518 line 2
squash da88b72 line 3
pick adc8ebd Demo 1 and 2

Follow the instructions and complete the rebase process. Now we only have two commits left:

dc5d518 - (HEAD -> master) Demo 1 and 2 (2021-06-28 20:07:19 +0800) <jdhao>
5452bfe - Line 1, 2 and 3 (2021-06-28 20:07:04 +0800) <jdhao>

Note that if we change the parent commit, the hash of any following commits also changes.
The commit has for Demo 1 and 2 changes from adc8ebd to 5452bfe.

Avoid squashing commits that have been pushed to remote

It is now rather clear that rebasing will change the commit history.
If you are collaborating with other people and have pushed some commits to the remote,
and your co-workers have already check out those commits, it is best to avoid rebasing those commits.
Otherwise, your co-workers would be in serious trouble!

References

  • squash the first two commits in git: https://stackoverflow.com/a/598788/6064933
  • Git squash older commits: https://stackoverflow.com/a/56262851/6064933
  • Git merging vs rebasing: https://www.atlassian.com/git/tutorials/merging-vs-rebasing

Author
jdhao

LastMod
2022-01-09

License
CC BY-NC-ND 4.0

Интерактивная перебазировка представляет коммиты в порядке, обратном тому, к которому вы привыкли при использовании git log . git rebase -i воспроизводит выбранные коммиты в точном (сверху вниз) порядке, в котором они перечислены в сохраненном файле инструкций по перебазированию. При сжатии фиксация, выбранная для сжатия, объединяется с фиксацией, которая предшествует ему в (отредактированном) списке, то есть фиксацией из предыдущей строки. В вашем случае — предыдущей фиксации для 56bcce7 . Вы должны сделать одно из следующих

  • git rebase -i HEAD~3 (если вы хотите раздавить 56bcce7 в 684f917 )
  • Если вы хотите объединить 56bcce7 с e43ceba , а e43ceba не зависит от 56bcce7 , просто измените их порядок:

    r e43ceba Lint.py: Replace deprecated link
    s 56bcce7 Closes #2774
    

    ОБНОВЛЕНИЕ : ответ Гаса ниже предлагает лучший способ сделать то же самое, не меняя порядок двух коммитов:

    r 56bcce7 Closes #2774
    s e43ceba Lint.py: Replace deprecated link
    

    Это сведет / объединит два коммита в один. Когда интерактивная перебазировка запрашивает измененное сообщение фиксации для 56bcce7 , предоставьте сообщение фиксации, описывающее объединение 56bcce7 и e43ceba .

У меня была аналогичная проблема, которую я решил следующим образом:

Это группа коммитов, которую я хотел раздавить:

1 s 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 pick 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

Как видите, я не хотел. 4, но у 1, 2 и 3 не было предыдущего коммита, в который можно было бы сквошить . Следовательно, невозможно «сжать» без предыдущей ошибки

Мое решение заключалось в использовании опции r для # r, reword = use commit, but edit the commit message

Итак, мой список коммитов выглядел так:

1 r 01cc5a08 Removes open div
2 s a2b6eecf Restores old fonts
3 s 603479ff Cleans left out div
4 s 5afdbc33 Update: show logo on landing page
5 s 04c1cb13 change version of dev and prod from 1 to 2
6 s bbe6a8f8 Update: show logo on landing page if they have one
7 s c0d6008a Adds check for C users

После сохранения интерактивная оболочка попросила меня переформулировать выбранный коммит.

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

У меня была эта проблема, и причина, по которой это произошло в моем случае, заключалась в том, что вы не можете раздавить старые коммиты на новый коммит. Вот пример, скажем, у вас есть 3 коммита:

1 pick 01mn9h78 The lastest commit
2 pick a2b6pcfr A commit before the latest
3 pick 093479uf An old commit i made a while back

Теперь, если вы скажете git rebase -i HEAD~3 и сделаете что-то вроде

1 pick 01mn9h78 The lastest commit
2 s a2b6pcfr A commit before the latest
3 s 093479uf An old commit i made a while back

Это приведет к ошибке:

ошибка: невозможно «сжать» без предыдущей фиксации. Вы можете исправить это с помощью «git rebase —edit-todo», а затем запустить «git rebase —continue». Или вы можете отменить перебазирование с помощью git rebase —abort.

Решение :

При сжатии коммитов вы должны свешивать недавние коммиты на старые, а не наоборот, поэтому в примере это будет примерно так:

1 s 01mn9h78 The lastest commit
2 s a2b6pcfr A commit before the latest
3 pick 093479uf An old commit i made a while back

Это будет работать нормально, если вам нужны все сообщения о фиксации, я бы предложил исправить вместо сквоша .

Сквош с обратной логикой . На более позднем этапе вы сможете выбрать желаемое сообщение фиксации.

  • pick первая фиксация, для которой вы не хотите получать сообщение фиксации.
  • squash или fixup коммит (ы), которые вы хотите объединить, до тех пор, пока не появится тот, который имеет сообщение о фиксации, которое вы действительно хотели.
pick 56bcce7 Closes #2774
squash e43ceba Lint.py: Replace deprecated link
  • подтвердите изменение ( :x )
  • удалить сообщение фиксации (ы) , которые вы не хотите , и оставить только сообщение от фиксации , что вы хотите (в данном случае: Lint.py: Replace deprecated link ).
  • подтвердите выбор ( :x )

Надеюсь, кому-то станет понятнее ✌🏽

Лучше всего просто сказать в интерактивном редакторе, содержащем коммиты, git всегда сквош снизу вверх, и нужно оставить запись «выбрать» вверху, чтобы получать сквош снизу.

Я просто попробовал такой подход.

git log -n3

Это покажет последние 3 коммита, которые дадут мне представление о том, какой коммит последний, а какой был ранее. Теперь заявил ребазинг,

git rebase -i HEAD ~ 3

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

выберите commit_id

Для двух других идентификаторов фиксации измените их на,

сквош commit_id

или просто,

s commit_id

Я также уже встречался с этой проблемой только сейчас, это просто небрежно. Вы можете решить проблему следующим образом: когда вы пытаетесь раздавить первую (56bcce7) и выбрать вторую, вы должны добавить «s» перед второй строкой, но не первый. вы также можете сослаться на следующий веб-сайт: http://backlogtool.com/git-guide/en/stepup/stepup7_5.html

That’s one thing I’ve just recently learned, and I’m writing down here for future reference.

Git’s rebase command permits to change the history of a git repository.

Note that this is safe to do only for commits that haven’t already been shared
to the team you are working with.

Why is it useful?

Consider the following scenario:

bruno ~/git/hello-git
$ git hist
# git hist is a custom alias for "git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
* 8a3a015 2016-10-26 | The most perfect commit in the history of the Universe (HEAD -> master) [brunoscopelliti]
* a48ca6d 2016-10-26 | Init repo [brunoscopelliti]

I’m so proud of myself… I’ve just pushed the best commit in the whole history of the Universe!

I should absolutely show someone else my greatness, so it’s time for a code review…
and it turns out that the commit was not so perfect as I thought.
However the fix is easy, and so we arrive here:

bruno ~/git/hello-git
$ git hist
* bd2b8f4 2016-10-26 | Ops, I've forgot something... Fixed now (HEAD -> master) [brunoscopelliti]
* 8a3a015 2016-10-26 | The most perfect commit in the history of the Universe [brunoscopelliti]
* a48ca6d 2016-10-26 | Init repo [brunoscopelliti]

It works, but the history is a bit messy, and going down this path, it won’t get better!

Git gives the option to squash two, or more commits into a single one using the rebase command.

How to squash commits?

Let’s try to squash those commits into a single one.

bruno ~/git/hello-git
$ git rebase -i HEAD~2

This cause your default Git editor — on my macbook pro, it is nano — to show up
with the following content:

pick 5e170e0 The most perfect commit in the history of the Universe
pick bd2b8f4 Ops, I've forgot something... Fixed now
# Rebase a48ca6d..bd2b8f4 onto a48ca6d (2 commands)
#
# 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 log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

We’re seeing only the last two commits cause I expressly requested to rebase only
the last two (remember the HEAD~2).

The comments make pretty clear how to proceed; so I change the content.

pick 5e170e0 The most perfect commit in the history of the Universe
squash bd2b8f4 Ops, I've forgot something... Fixed now

Save, exit. Git will give you a reassuring successfull message at the end of the process.

bruno ~/git/hello-git
$ git rebase -i HEAD~2
[detached HEAD 7c442c5] The most perfect commit in the history of the Universe
 Date: Wed Oct 26 18:31:15 2016 +0200
 1 file changed, 10 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.

However let’s check.

bruno ~/git/hello-git
$ git hist
* 7c442c5 2016-10-26 | The most perfect commit in the history of the Universe (HEAD -> master) [brunoscopelliti]
* a48ca6d 2016-10-26 | Init repo [brunoscopelliti]

You may have noted that in the process the hash of the commit with the message
The most perfect commit in the history of the Universe has changed.
That’s because this process actually generates brand new commits with completely
different IDs than the old commits, and leaves the old commits where they were.

bruno ~/git/hello-git
$ git cat-file -t bd2b8f4
commit

Common pitfalls

It’s not possible to squash a commit without a previous commit. So if you try this one

squash 5e170e0 The most perfect commit in the history of the Universe

pick bd2b8f4 Ops, I've forgot something... Fixed now

Git will throw an error at you.

bruno ~/git/hello-git
$ git rebase -i HEAD~2
error: Cannot 'squash' without a previous commit
You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
Or you can abort the rebase with 'git rebase --abort'.

However Git is also so kind that suggest how to solve the issue:

git rebase --edit-todo will reopen the editor and give you a chance to review,
then git rebase --continue will restart the rebase process.

Понравилась статья? Поделить с друзьями:
  • Error cannot split rhs for multiple assignment
  • Error cannot set write parameters mode page
  • Error cannot satisfy dependencies ошибка при установке пакетов
  • Error cannot satisfy dependencies ubuntu
  • Error cannot satisfy dependencies kubuntu