Configuring Github Templates to Merge From Upstream

Posted on April 8, 2022 by Sean Sosik-Hamor ‐ 4 min read

Configuring Github Templates to Merge From Upstream

GitHub Templates Diverge From Upstream Repositories

Creating a repository from a template starts a new repository with a single commit and no history. This is perfect for starting brand-new, unrelated projects, but makes it difficult to merge changes and updates from upstream.

For example, generating a new repository from a Jamstack GitHub template can get a site up and running quickly, but it may be difficult to merge future Jamstack theme updates and changes. This can be solved by associating the remote, upstream repository before making any changes.

$ git remote -v
origin    https://github.com/SeanSosikHamor/Website-Sciri.net-hugo-doks.git (fetch)
origin    https://github.com/SeanSosikHamor/Website-Sciri.net-hugo-doks.git (push)
upstream    https://github.com/h-enk/doks.git (fetch)
upstream    https://github.com/h-enk/doks.git (push)

Starting Fresh With a Brand-New GitHub Template

Starting fresh is the path of least resistance, and doesn’t require any time-consuming merge operations if the remote upstream is specified before local changes are made.

Using the Jamstack doks theme as an example, create a new repository from the template, clone the newly-created repository, then specify the remote upstream:

$ git clone https://github.com/SeanSosikHamor/doks-remote-upstream-example.git
$ cd doks-remote-upstream-example
$ git remote add upstream https://github.com/h-enk/doks

Check to make sure that the new upstream remote has been added:

$ git remote -v
> origin    https://github.com/SeanSosikHamor/doks-remote-upstream-example.git (fetch)
> origin    https://github.com/SeanSosikHamor/doks-remote-upstream-example.git (push)
> upstream    https://github.com/h-enk/doks (fetch)
> upstream    https://github.com/h-enk/doks (push)

Treat the newly-created repository as a fork, and start the sync with upstream:

$ git fetch upstream
$ git checkout master

This is where things get tricky, because a template is not a fork, and has an unrelated git history:

$ git merge upstream/master
> fatal: refusing to merge unrelated histories

The error can be resolved by toggling the allow-unrelated-histories switch:

$ git pull origin master --allow-unrelated-histories
>  * branch            master     -> FETCH_HEAD
> Already up to date.

$ git pull upstream master --allow-unrelated-histories
> From https://github.com/h-enk/doks
>  * branch            master     -> FETCH_HEAD
> Merge made by the 'recursive' strategy.

If there were no local changes, then the merge will immediately commit and prompt for a commit message. The merge can now be pushed back to the newly-created repository:

$ git status
> On branch master
> Your branch is ahead of 'origin/master' by 766 commits.
>   (use "git push" to publish your local commits)
> 
> nothing to commit, working tree clean

$ git push
> Enumerating objects: 3530, done.
> Counting objects: 100% (3528/3528), done.
> Delta compression using up to 16 threads
> Compressing objects: 100% (1418/1418), done.
> Writing objects: 100% (3379/3379), 2.68 MiB | 8.05 MiB/s, done.
> Total 3379 (delta 2004), reused 3266 (delta 1901), pack-reused 0
> remote: Resolving deltas: 100% (2004/2004), completed with 104 local objects.
> To https://github.com/SeanSosikHamor/doks-remote-upstream-example.git
>    77a68ec..399014f  master -> master

Merging With Upstream After Changes Have Been Made

If a project has already been customized and deployed, it may be tedious after the fact to manually merge in all the changes where the customized repository has diverged from the upstream repository. Every local file that’s been modified will be treated as a change, even if the upstream file hasn’t been changed since the initial template was cloned.

$ git remote add upstream https://github.com/h-enk/doks
$ git remote -v                                        
> origin    https://github.com/SeanSosikHamor/Website-Sciri.net-hugo-doks.git (fetch)
> origin    https://github.com/SeanSosikHamor/Website-Sciri.net-hugo-doks.git (push)
> upstream    https://github.com/h-enk/doks (fetch)
> upstream    https://github.com/h-enk/doks (push)

$ git fetch upstream
$ git checkout master
$ git merge upstream/master
> fatal: refusing to merge unrelated histories

$ git pull upstream master --allow-unrelated-histories
> From https://github.com/h-enk/doks
>  * branch            master     -> FETCH_HEAD
> CONFLICT (add/add): Merge conflict in package-lock.json
> Auto-merging package-lock.json
> CONFLICT (add/add): Merge conflict in netlify.toml
> Auto-merging netlify.toml
> CONFLICT (add/add): Merge conflict in layouts/sitemap.xml
> Auto-merging layouts/sitemap.xml

Resolving Git Merge Conflicts

There are many workflows and personal preferences when it comes to resolving merge conflicts, and GitHub has a basic tutorial.

Other Methods

The above steps essentially convert a template into a fork, which may not be desired. Other methods have been outlines by other developers: