In this article
You want to deploy a website update, but git pull --ff-only stops. This often is not a download failure: the local branch and remote have acquired different new commits. A simple fast-forward cannot integrate both developments.
That happened to my worldnode checkout on 6 October 2026. The server had its own World Observer dashboard commit while GitHub contained a website change. A subsequent rebase succeeded. This real example makes the terminology easier to understand.
What fast-forward means
A fast-forward moves the local branch pointer along an existing history. It requires no additional local development that needs to be integrated with the target.
When a local commit and a remote commit both appear after a shared starting point, neither automatically replaces the other. They are not yet arranged in one sequence. --ff-only requires the simple case and stops otherwise.
| State | What an update needs |
|---|---|
| Only remote commits are new | A fast-forward may be possible |
| Only local commits are new | The remote may already be included |
| Both sides have their own commits | A deliberate integration choice |
| Uncommitted local files exist | Understand their state and purpose first |
Check the working tree first
For my website checkout, the read-only check is:
git -C /srv/www/dennishilk.github.io status
This path belongs to my server; adapt it to your checkout. A clean working tree is a useful prerequisite. First decide what uncommitted changes mean: commit them deliberately or preserve them separately. Automatically staging everything can include unrelated or sensitive files.
Then retrieve remote information:
git -C /srv/www/dennishilk.github.io fetch origin main
fetch updates known remote data without integrating it into the current working tree. Check that origin identifies the intended repository. An outdated origin/main can make a comparison misleading.
Compare local and remote history
List commits unique to either side:
git -C /srv/www/dennishilk.github.io log --oneline --left-right -15 HEAD...origin/main
My recorded case contained these two entries:
< 843d8827 world-observer: publish dashboard 2026-10-06
> 3fa0277a Restore homepage styling and unify the site language toggle
< identified the local HEAD side and > the origin/main side. These hashes and titles describe the observed 6 October case, rather than expected output on every server. The objective was clear: retain the dashboard change and add the website update.
Inspect the actual changes before selecting an integration method when needed. Commit titles alone do not explain every file or effect.
Choose between merge and rebase
A merge connects histories while preserving existing commit identities. For genuinely diverged histories, it normally adds a merge commit. This suits work where the original branching history should remain visible.
A rebase reapplies local commits onto the selected new base. The resulting commits receive new identities. In the server case, the dashboard change followed the website update afterwards.
| Choice | Typical purpose |
|---|---|
| Merge | Preserve history already shared with others |
| Rebase | Place your unshared local commits on a new base |
--ff-only |
Allow only the simple fast-forward path |
Do not casually rebase published commits: other computers may already build on them. The server approach suited that local commit; it does not imply that every diverged shared history should be rewritten.
Preserve a local reference and rebase
With a clean status, you can create a local reference before integration. Choose an unused name:
git -C /srv/www/dennishilk.github.io branch backup-vor-update-20261008
This preserves the current commit as a comparison point. It does not preserve uncommitted files or provide an independent data backup.
The actual server integration used:
git -C /srv/www/dennishilk.github.io rebase origin/main
If the changes fit together, Git completes successfully. Then check git status, the relevant website behaviour and the history. Successful Git integration does not establish that every application feature was tested.
Resolve conflicts or abort deliberately
If Git stops, git status identifies conflicted files. Read both changes and decide on the intended final result. For HTML, resolving a conflict involves preserving navigation, language links and structure as well as removing markers.
Stage each deliberately resolved file individually. PATH/TO/FILE is a placeholder:
git -C /srv/www/dennishilk.github.io add PATH/TO/FILE
Continue the running rebase:
git -C /srv/www/dennishilk.github.io rebase --continue
To return to the beginning of that ongoing rebase instead:
git -C /srv/www/dennishilk.github.io rebase --abort
--abort applies to an unfinished rebase. It is not a universal undo command after every completed update. --skip omits a change; do not use it merely to dismiss a conflict.
Understand regular server updates and their limits
If the server intentionally creates its own unshared dashboard commits, an explicitly selected pull with rebase is understandable:
git -C /srv/www/dennishilk.github.io pull --rebase origin main
This can still stop at conflicts or local changes. It is not a reason to use reset --hard, force-push or delete files wholesale. Inspect the update, retain useful local work and test the result.
References
- Git pull: fetch and selected integration.
- Git rebase: a new base, conflicts and aborting.
- Git merge and Git log.
- Website repository.
The server example is from 6 October 2026; references were checked on 8 October 2026. Run commands only in the checkout you have inspected.