For me, git repositories are a complex thing. As I wrote in an earlier article I can keep my local repository up-to-date to the original one and simply manage my changes to a github repository saved in my github account. So I can distribute my changes on many computers at home.

But all these changes make the structure of the git commit history more complex. I thought I could shrink my own commits down to a minimum and just summarize them into one single commit on the top of the commit history in my github repository.

Time will tell, if this is a good approach…

I am no expert and this article helps me remembering this scenario with multiple repositories and their commit history. The logic behind this might not be correct, so if you have a better solution for this specific scenario, please let me know. I’m all up to learn this stuff the right way. {:.note}

If you have commits in your history

Let’s take my prezto repository and add the upstream repository to it.

 1$ git log
 2commit d321120fdebb84c7dc282a05eaaa57fdaf56987b (HEAD -> master)
 3Author: Dominic Reich <dominic>
 4Date:   Mon Dec 21 10:33:44 2020 +0100
 6    remove dot-expansion as this is also done when doing multiline-commands
 7      like doing git commit with single quotes and making line brakes
 8      (like this git commit here../..)
10commit aa85e03e8aced6832a77307c77aa77ce34dc462e
11Author: Dominic Reich <dominic>
12Date:   Mon Dec 7 07:15:45 2020 +0100
14    adds aliases on darwin; fix ostype for raspberry pi
16commit 63569c97a25c3910dfb1dad5a0e170850c5b4b67
17Author: Dominic Reich <dominic>
18Date:   Sun Nov 29 20:41:37 2020 +0100
20    adds zaliases
22commit 3877758908c60e24d4dce093ff8570135b7dfb19
23Author: Dominic Reich <dominic>
24Date:   Sat Nov 28 19:26:00 2020 +0100
26    personalized
28commit b7a80d99a84e718f30a076b27b090d3e998ad135 (upstream/master, origin/master, origin/HEAD)
29Author: Roman Perepelitsa <roman.perepelitsa>
30Date:   Fri Dec 18 16:38:31 2020 +0100
32    prompt: update powerlevel10k submodule to v1.14.4
34    Release notes:
36    [...]

In this example I already have a commit that changes something. And a few commits later the changes will be reverted again because that did not work out so well.

This is why we will extract those last commits into a patchfile and re-apply the file in one single run after we updated our local repository to the state of the upstream repository. We will also force the github repository (origin) to the same state as our upstream repository, so we will end up having only one commit on top of the history on github.

Now, create our patchfile.

1$ git format-patch -5 HEAD > ../changes.patch

This saves the recent 5 commits from HEAD into ../changes.patch, which file is not in the git repository, but one directory above.

We continue using the master branch and getting upstream/master.

 1$ git checkout master
 2Bereits auf 'master'
 3Ihr Branch ist auf demselben Stand wie 'origin/master'.
 4$ git pull upstream master
 5Warning: Permanently added ',' (RSA) to the list of known hosts.
 7 * branch            master     -> FETCH_HEAD
 8Bereits aktuell.
 9$ git reset --hard upstream/master
10HEAD ist jetzt bei b7a80d9 prompt: update powerlevel10k submodule to v1.14.4
11$ git push origin master --force

Now we have our local master branch set equal to the master branch on the remote upstream repository. We now add our changes into the repository, our patchfile.

 1$ patch -p1 < ../changes.patch
 2patching file runcoms/zlogout
 3patching file runcoms/zpreztorc
 4patching file runcoms/zprofile
 5patching file runcoms/zshrc
 6patching file runcoms/zaliases
 7patching file runcoms/zaliases
 8patching file runcoms/zpreztorc
 9patching file runcoms/zaliases
10patching file runcoms/zshenv

This leaves us again with many changes, but we can add them to git all together as one single commit if we like.

Run git diff to view the changes on the files.

Make your changes now and create a new commit.

 1$ git add .
 2$ gcm 'personalized'
 3[master 3d19001] personalized
 4 6 files changed, 195 insertions(+), 26 deletions(-)
 5 create mode 100644 runcoms/zaliases
 6 rewrite runcoms/zlogout (68%)
 7$ git push origin
 8Warning: Permanently added ',' (RSA) to the list of known hosts.
 9Objekte aufzählen: 16, Fertig.
10Zähle Objekte: 100% (16/16), Fertig.
11Delta-Kompression verwendet bis zu 8 Threads.
12Komprimiere Objekte: 100% (9/9), Fertig.
13Schreibe Objekte: 100% (9/9), 3.94 KiB | 3.94 MiB/s, Fertig.
14Gesamt 9 (Delta 5), Wiederverwendet 0 (Delta 0), Pack wiederverwendet 0
15remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
17   b7a80d9..3d19001  master -> master

The history now looks like this:

 1$ git log
 2commit 3d19001518997c29204457fe2d1119fc9830010c (HEAD -> master, origin/master, origin/HEAD)
 3Author: Dominic Reich <dominic>
 4Date:   Mon Dec 21 12:08:25 2020 +0100
 6    personalized
 8commit b7a80d99a84e718f30a076b27b090d3e998ad135 (upstream/master)
 9Author: Roman Perepelitsa <roman.perepelitsa>
10Date:   Fri Dec 18 16:38:31 2020 +0100
12    prompt: update powerlevel10k submodule to v1.14.4
14    Release notes:
16    [...]

Approach #2: reset to upstream branch

 1$ git checkout main
 2Bereits auf 'main'
 3Ihr Branch ist auf demselben Stand wie 'origin/main'.
 4$ git reset upstream/main
 5Nicht zum Commit vorgemerkte Änderungen nach Zurücksetzung:
 6M html/js/config.js
 7M logtailer.ini
 8$ git diff > ../changes.patch
 9$ git reset --hard upstream/main
10HEAD ist jetzt bei ea48f75 Added Key Features in
11$ git push origin main --force
12Gesamt 0 (Delta 0), Wiederverwendet 0 (Delta 0)
14 + b06f675...ea48f75 main -> main (forced update)
15$ patch -p1 < ../changes.patch
16patching file html/js/config.js
17patching file logtailer.ini
18$ git add .
19$ git commit -m 'personalize'
20[main 4789400] personalize
21 2 files changed, 7 insertions(+), 6 deletions(-)
22$ git push
23Warning: Permanently added ',' (RSA) to the list of known hosts.
24Objekte aufzählen: 11, Fertig.
25Zähle Objekte: 100% (11/11), Fertig.
26Delta-Kompression verwendet bis zu 4 Threads.
27Komprimiere Objekte: 100% (6/6), Fertig.
28Schreibe Objekte: 100% (6/6), 1.26 KiB | 646.00 KiB/s, Fertig.
29Gesamt 6 (Delta 3), Wiederverwendet 0 (Delta 0)
30remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
32   ea48f75..ba62875  main -> main

You now have the history of your upstream repository and only one commit on top of the history.

PS: I’ve removed the full email addresses on git log outputs.