Push rejected and a polluted fork main under a fork-and-MR policy
Alice maintains a fork, alice/api, of group/api. She fetched upstream but then made two feature commits directly on her local main. She also ran git pull origin main, which created a merge commit, and pushed her main to her fork. She opened a merge request from alice:main into group:main. The upstream main is protected, five commits ahead, and requires linear history with no merge commits. The MR now shows eight commits, including the merge commit and three commits that are already upstream. Alice has permission to force-push her fork but not the upstream project. She needs an MR containing only her two feature commits, based on current upstream/main. Her fork's default branch is main, and the fork is public. The upstream project uses fast-forward merges only and deletes source branches after merge.
1. Give the command sequence to produce a clean feature branch and restore the fork's `main`, explaining what each step changes and what must not be forced.
Read the worked answer
Preserve the two feature commits before touching main. Identify their SHAs:
git switch main
git log --oneline --decorate -10
Create the clean branch from current upstream, then cherry-pick only those two commits:
git fetch upstream
git switch -c fix/api-retry upstream/main
git cherry-pick <sha1> <sha2>
If a cherry-pick conflicts, resolve it and continue; this is expected because the old base differed. Verify:
git log --oneline upstream/main..HEAD
git diff --stat upstream/main...HEAD
There should be exactly two commits and only the intended files. Now repair the fork's main. This is safe because it is Alice's fork, not the protected upstream:
git switch main
git reset --hard upstream/main
git push --force-with-lease origin main
git push -u origin fix/api-retry
--force-with-lease refuses the push if the fork's main changed since Alice last fetched, preventing an accidental overwrite. Never run this against upstream. Open a new MR from fix/api-retry to group/api:main.
2. Why does the MR show commits that are already upstream, and why is the merge commit a problem for the target branch's linear-history rule? How would you verify the final MR contains only two commits and the intended diff?
Read the worked answer
The MR compares alice:main with group:main using the merge base. Alice's branch was based on an older main, so its history still contains upstream commits that have since been merged upstream. Git shows them because they are not on the target's current first-parent path, even though their patches are already present. The merge commit from git pull origin main is an extra commit that the target's linear-history rule rejects, and it makes the MR look larger than it is. This is a topology problem, not a content problem.
After rebuilding on upstream/main, verify:
git fetch upstream
git log --oneline --no-merges upstream/main..HEAD
git diff --stat upstream/main...HEAD
git show --stat HEAD
upstream/main..HEAD lists only commits reachable from the branch and not from upstream. The three-dot diff upstream/main...HEAD shows changes since the merge base, which is what the MR should display. If the log still shows upstream commits, the branch was started from the wrong base or a cherry-pick pulled in a merge. Do not fix this by force-pushing upstream; reopen the MR from the corrected branch and close the old one.
3. A later `git push --force-with-lease origin main` fails even though Alice expects the fork's `main` to be unchanged. What are the likely causes and how should she proceed without using `--force`?
Read the worked answer
--force-with-lease fails when the remote-tracking ref for origin/main is stale, for example because the fork was updated by the web UI or another machine. The lease compares the remote's current value with the expected value stored in refs/remotes/origin/main. Do not fall back to --force. First fetch and inspect:
git fetch origin
git log --oneline --decorate origin/main -5
git log --oneline main..origin/main
If the new remote commits are only the old feature commits from the previous push, Alice can reset main to upstream/main again and retry --force-with-lease. If someone else added commits to the fork's main, preserve them on a separate branch first:
git branch backup/fork-main origin/main
Then reset and force-push. In a shared repository, force-pushing a branch that others use is a coordination failure. Here the fork's main should not be a working branch at all. Set the fork's default branch to a clean branch or stop committing to main, and use --force-with-lease only on the feature branch after review-driven rebases.