Keywords: Git push | Force push | History rewriting | Non-fast-forward push | Server configuration
Abstract: This paper provides an in-depth analysis of non-fast-forward push rejection issues encountered after using git reset --hard. Through detailed scenario reconstruction, it explores server configuration limitations, history rewriting strategies, and alternative solutions. The article systematically explains core concepts including receive.denyNonFastForwards configuration, various force push methods, branch deletion and recreation techniques, and using git revert as a safe alternative, offering developers a comprehensive problem-solving framework.
Problem Scenario Reconstruction
In typical Git workflows, developers may encounter situations requiring the reversal of a series of bad commits. Assuming the current repository commit history is A-B-C-D-E-F, where D, E, and F are commits that need to be undone. By executing the git reset --hard C command, the local repository's master branch reverts to commit C, while creating an old_master branch to preserve the complete D-E-F commit history.
The local repository state at this point is:
A-B-C master
\ D-E-F old_master
During subsequent development, the developer extracts useful portions from commits D-E-F via cherry-pick, forming new commits G and H, resulting in the final local repository state:
A-B-C-G-H master
\ D-E-F old_master
Root Cause of Push Failure
When attempting to push local modifications to the remote repository, Git rejects this non-fast-forward push operation. The error message typically displays:
error: denying non-fast forward refs/heads/master (you should pull first)
To git@git.example.com:myrepo.git
! [remote rejected] master -> master (non-fast forward)
This rejection behavior stems from Git's security mechanism design. Since the local history has been rewritten through reset operations, creating a divergence from the remote repository's A-B-C-D-E-F history, Git cannot reconcile this historical divergence through simple fast-forward merging.
Server Configuration Limitations Analysis
Even when using git push --force or git push --force origin master commands, the push may still be rejected. This typically indicates that the remote server is configured with the receive.denyNonFastForwards parameter, which explicitly prohibits non-fast-forward push operations to protect repository history integrity.
In team collaboration environments, this configuration holds significant security implications:
- Preventing accidental history rewrites that could cause other team members to lose work
- Maintaining traceable code change history
- Ensuring effectiveness of code review and issue tracking
Traditional Solution: Branch Deletion and Recreation
In older versions of Git, restrictions could be bypassed through branch deletion and recreation:
# Delete remote master branch
git push origin :master
# Recreate and push local master branch
git push origin master
While this method is effective, it carries significant risks:
- During the brief window between deletion and recreation, other developers may be unable to access the branch
- May trigger abnormal behavior in automated deployment systems
- Could be blocked by permission policies in strictly controlled environments
Safe Alternative: Using Git Revert
When server configuration cannot be modified or when a safer approach is preferred, using the git revert command is recommended. This method creates new commits to undo specific changes rather than rewriting history:
# Create revert commits on top of original history
git revert D^..F
The resulting commit history becomes:
A-B-C-D-E-F-[(D-E-F)^-1] master
Advantages of this approach include:
- Maintaining history integrity with all original commits remaining traceable
- Compatibility with all Git server configurations without requiring special permissions
- Support for team collaboration without affecting other developers' work
- Providing clear reversal records for easier problem troubleshooting
Extended Practical Application Scenarios
Referencing community discussion cases, developers encountering situations where the remote repository is ahead after executing git reset --hard should follow this correct processing flow:
# First pull the latest remote changes
git pull origin master
# Resolve potential merge conflicts
# Then perform necessary code adjustments
# Finally push modifications
git push origin master
This method ensures the local repository remains synchronized with the remote, avoiding historical divergence issues, particularly suitable for use in continuous integration environments.
Best Practice Recommendations
Based on understanding Git push mechanisms, development teams are advised to:
- Clearly define history rewriting strategies and permission controls during project initialization
- Exercise caution when using history rewriting operations on shared branches
- Prioritize non-destructive reversal methods like
git revert - Establish clear code review and merge processes
- Configure appropriate security restrictions in continuous integration environments
By systematically understanding Git push mechanisms and server configurations, developers can more effectively manage code history, balancing development flexibility with project stability requirements.