The first of your three highlighted commits is a merge. If you keep it, you must (really, it's not an option) keep the 5 commits you don't want. (A shallow clone can omit them until deepened but they remain logically present, even if not physically.)
Thus, what you need to do is make a new commit to which you can point master
and origin/master
, that is "as good" as the merge, but is not itself a merge, and simply has the middle of your three commits-to-retain as its parent.
The "cleaning up" and "frontend polish" commits can remain as-is; they just need a new commit that points back to them, that uses the same tree as the merge. (You can also keep the original commit message, or make a new one.) Git being git, there are a lot of ways to achieve this. The fewest-commands method is to use git commit-tree
1 but let's look at a method I think is more straightforward and understandable.
First, we start by creating a new branch, pointing to the second to-keep commit, the "frontend polish" one. (This also assumes the current work-tree is clean.)
$ git branch newmaster master^2 # master^ if 1st parent
$ git checkout newmaster
Now we want to make a new commit, using the source tree stored with the merge, that is not itself a merge. We begin by scrubbing away everything (so that any files removed during the merge, remain removed when we're done—if you're sure nothing got removed you can skip this step). Then, we check out all the files associated with the latest commit on master
, i.e., the merge result. This assumes you're in the top level directory:
$ git rm -r . # empties work-tree and index
$ git checkout master -- . # repopulates work-tree and index
This form of checkout
extracts from the given commit (master
's tip-most commit) the given path(s)—in this case, .
, which is the top level of the repository and therefore includes every path in the commit. Each extracted file is also entered into the index during the extraction, so we now have the new commit ready to go.
The only thing left to do is to recover the merge's message, if desired:
$ git log -1 master > /tmp/msg
(or similar), then:
$ git commit
This makes a new commit on newmaster
, whose parent is the "frontend polish" commit, whose parent is "cleaning up", which has no more parents. Now you just need to get this to be branch master
, and that's pretty trivial:
$ git branch -D master # forcibly discard old master entirely
$ git branch -m master # rename newmaster to master
At this point you can force-push to origin
to replace master
on the server (but be sure everyone else sharing the server's version is OK with this, and that there are no new commits there to worry about). When the server's master
is updated, your origin/master
(copied back from the server) will point to your master
again (and at that point you can set your master's upstream to origin/master
as usual).
For reference, this takes just 2 to 4 git commands, depending on how much you want to retain, how much you want to write from scratch, and how you count commands here:
$ git log -1 --format=%B master > /tmp/logmsg
(now edit /tmp/logmsg
as desired)
$ newid=$(git commit-tree -p master^{tree} < /tmp/logmsg)
(at this point, make sure it worked, e.g., git show $newid
)
$ git update-ref -m "reset to new non-merge tip" master $newid
(and now you have master
pointing to the new copied commit that has only the one parent).