1

I am experimenting with git reset and get some results I don't understand.

First lets check git log --oneline:

bebf9f0 (HEAD -> main, [MyRepository]/main) Merge branch 'main' of https://github.com/[MyRepository] into main
b20fac7 Merge branch 'main' of https://github.com/[MyRepository] into main   # AFTER RESET
7730202 Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
dc8f500 Test amend.                                                          # COMMIT REMOVED DURING RESET
705cfac Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
c8971ef Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
bd2e51f Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
8aa0cc0 Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
5d29b9f Merge branch 'main' of https://github.com/[MyRepository] into main   # COMMIT REMOVED DURING RESET
62d3662 Merge branch 'main' of https://github.com/[MyRepository] into main
13eb820 Merge branch 'main' of https://github.com/[MyRepository] into main
3ad7a4f Changed comments Testen Untar
16e44fe Changed comments Testen Untar
ae10673 Add Week01 "Testen Untar" - big files ignored
2096cfb Test opruimen
78cfc4c Test2
6210855 Test1
bb7fa9a Test
85a1a7a Update .gitignore
bfe32c6 Update gitignore
2d2277c Add Week01 Testen Unzip
9290c15 Initial

Then I do a git reset HEAD~1 and check git log --oneline and git status:

b20fac7 (HEAD -> main) Merge branch 'main' of https://github.com/[MyRepository] into main
62d3662 Merge branch 'main' of https://github.com/[MyRepository] into main
13eb820 Merge branch 'main' of https://github.com/[MyRepository] into main
3ad7a4f Changed comments Testen Untar
16e44fe Changed comments Testen Untar
ae10673 Add Week01 "Testen Untar" - big files ignored
2096cfb Test opruimen
78cfc4c Test2
6210855 Test1
bb7fa9a Test
85a1a7a Update .gitignore
bfe32c6 Update gitignore
2d2277c Add Week01 Testen Unzip
9290c15 Initial

On branch main
Your branch is behind '[MyRepository]/main' by 8 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean

Git moved one commit back to b20fac7 which I expected.

However Git also removed the older commits 7730202,..., 5d29b9f and says I am 8 instead of 1 commit behind the remote repository which I don't understand. Does anyone know what happened?

Update

Following the suggestion from user j6t I ran git log --oneline --graph:

enter image description here

(And now Im trying to figure out what I am looking at..)

user10186832
  • 423
  • 1
  • 9
  • 17
Frank
  • 541
  • 1
  • 6
  • 18
  • 4
    Look at the output of `git log --oneline --graph`. I would expect that the removed commits live on the side branch that ends in `7730202` and was merged into `bebf9f0` as the second parent. – j6t Mar 02 '23 at 16:49
  • Thanks! I just added the results. Do you know why I see left and right branches? I thought I was on a straight timeline because I only used the branch "main". – Frank Mar 02 '23 at 17:21

2 Answers2

3

git models history as a "directed acyclic graph" - think of a tree, but where branches can split apart and then join back together again later. This is traced backwards by every commit knowing its "parents".

A normal commit simply has one parent: the commit you had checked out when you ran git commit.

Every time you perform a "true merge" (not using "squash merge", or allow "fast-forward"; you can read about those elsewhere), git creates a commit with two parents (or, in rare use cases, more than two):

  • The commit you are merging "onto" (e.g. what you have checked out, or the "base" on a Github Pull Request)
  • The commit you are merging "from" (e.g. the argument to git merge, or the "compare branch" on a Github Pull Request)

From those two parents, you can reach all of the history on both sides of the merge.

Let's imagine you checked out a branch that was at commit "C", and ran git merge F; git created a new merge commit "M", and the history graph looks like this:

... A <- B <- C <-\
                   +- M
... D <- E <- F <-/

Note that A, B, C, D, E, and F are all reachable following the "parent" pointers from M.

If you just run git log, or look in an over-simplified history view like Github's, those commits are simply mixed together in one list, in date order, something like this:

M ...
C ...
F ...
E ...
B ...
D ...
A ...

When you use git reset to go back to commit C, your history graph looks like this:

... A <- B <- C 

So the simplistic log looks like this:

C ...
B ...
A ...

Using git log --graph, you can see this more clearly (once you learn to read it). It would show the merged history more like this:

* M ...
|\
* | C ...
| |
| * F ...
| |
| * E ...
| |
* | B ...
| |
| * D ...
| |
* | A ...
| |

The idea is that the vertical lines are the two branches you merged; a * means the commit is on that branch - so C, B, and A are all on the left-hand branch, and F, E, and D are all on the right-hand branch.

Then when you git reset back to C, the right-hand branch isn't linked to your history any more, so you just have:

* C ...
|
* B ...
|
* A ...
|

A visual tool like GitKraken will also help view this history more clearly.

IMSoP
  • 89,526
  • 13
  • 117
  • 169
  • Thanks! I started to suspect this too but it is nice to see it written down so clearly. Before I though I was on a straight timeline because I didn't use branches other than "main". But now I realize that pull commands also create commits and that those commits can have multiple parents each with their own timeline. It has become a lot clearer! – Frank Mar 03 '23 at 13:54
1

Looking at your graph, the primary commits are denoted as an * on the far-left. Any branches to the right represent parent commits. And merge commits (e.g., Merge branch 'main' of https://github.com/[MyRepository] into main) are a commit consisting of one or more parent commits. So, you can see that the commits 8aa0cc0..7730202 are parents of the merge commit, bebf9f0.

So when you run git reset HEAD~1, Git sets the head of your repository from bebf9f0 to the next primary commit, b20fac7. As a result, your repository no longer has the next merge commit nor its parent commits. This is why git status reports you are 8 commits behind (1 merge commit + 7 parent commits).

If you wish to avoid merge commits moving forward, I recommend reviewing the difference between git merge and git rebase (reference 1, reference 2). You can also choose to only except fast-forward merges with git merge --ff-only or git pull --ff-only.

Garrett Hyde
  • 5,409
  • 8
  • 49
  • 55
  • 2
    Just as a counterpoint to "if you wish to avoid...", I think it's important to stress that merge commits are a very deliberate part of how git is built, not something you have to work around. Rebase and fast-forward are *also* useful, when used in the right place, but overusing them just to be "tidy" will also create problems. – IMSoP Mar 02 '23 at 18:29
  • 1
    I 100% agree. There are situations where it's better to use a merge commit instead of trying to rebase. And those situations are a common occurrence in Git workflows, such as merging work from multiple developers on the same source code. But in this specific instance, the branches have merge commits of merge commits, which might not be the desired outcome. – Garrett Hyde Mar 02 '23 at 18:41