pcottle/learnGitBranching · error · GitError
git-error-origin-fetch-no-ff
Error message
git-error-origin-fetch-no-ff
What it means
Thrown by GitEngine's fetch/push fast-forward check when the destination branch's current commit is not an ancestor of the source branch's commits. In real git this is the 'fetch first' / non-fast-forward rejection: the remote (or target) has commits that the local source does not contain, so updating the ref would lose history.
Source
Thrown at src/js/git/index.js:1030
* Origin stuff!
************************************/
GitEngine.prototype.checkUpstreamOfSource = function(
target,
source,
targetBranch,
sourceBranch,
errorMsg
) {
// here we are downloading some X number of commits from source onto
// target. Hence target should be strictly upstream of source
// lets first get the upstream set from source's dest branch
var upstream = Graph.getUpstreamSet(source, sourceBranch);
var targetLocationID = target.getCommitFromRef(targetBranch).get('id');
if (!upstream[targetLocationID]) {
throw new GitError({
msg: errorMsg || intl.str('git-error-origin-fetch-no-ff')
});
}
};
GitEngine.prototype.getTargetGraphDifference = function(
target,
source,
targetBranch,
sourceBranch,
options
) {
options = options || {};
sourceBranch = source.resolveID(sourceBranch);
var targetSet = Graph.getUpstreamSet(target, targetBranch);
var sourceStartCommit = source.getCommitFromRef(sourceBranch);
View on GitHub (pinned to 5b09d0ff96)
Solutions
- Pull or fetch and merge/rebase first so your source contains the target's commit
- Push to a different destination branch or reset it to match before pushing
- As a library consumer, pass the force/override option the surrounding API supports if history rewrite is intended
Example fix
// before
this.push('main', 'main'); // remote main diverged
// after
this.pull('main', 'main');
this.push('main', 'main'); Defensive patterns
Strategy: validation
Validate before calling
var upstream = Graph.getUpstreamSet(source, sourceBranch);
var targetId = target.getCommitFromRef(targetBranch).get('id');
if (!upstream[targetId]) {
// diverged: pull/rebase first instead of pushing Try / catch
try { engine.push(src, dest); } catch (e) { if (e instanceof GitError && /fast/i.test(e.msg)) { /* pull then retry */ } else throw e; } Prevention
- Always fetch before push in scripts
- Check ancestry of target commit before pushing
When it happens
Trigger: Calling fetch or push where target.getCommitFromRef(targetBranch) yields a commit id not in Graph.getUpstreamSet(source, sourceBranch) — e.g. pushing to a remote branch that has diverged, or fetching into a ref that already has newer commits, without force/options to override.
Common situations: Someone else (or another level/simulation step) advanced the remote branch and you try to push over it; in LearnGitBranching, attempting a non-FF push after the origin branch moved.
Related errors
- cannot fetch to ' + ref + ' when checked out on ' + ref
- ' + branchName + ' is not a branch!
- ' + branchName + ' is not a remote tracking branch! I don't
- --delete doesn't make sense without any refs
- --delete only accepts plain target ref names
AI-assisted analysis of pcottle/learnGitBranching@5b09d0ff96 (2026-08-27).
Data as JSON: /api/errors/7a234942b76f319b.
Report an issue: GitHub.