Most know that utilizing Git’s push --force
command is strongly discouraged and is taken into account damaging.
However, to me, it appeared very unusual to place all my belief in Git with my initiatives and on the similar time fully keep away from utilizing considered one of its widespread instructions.
This led me to analysis why people think about this command so dangerous.
Why does it even exist within the first place? And what occurs underneath the hood?
In this text, I share my discoveries so that you, too, can perceive the utilization and influence of this command in your challenge, study new safer alternate options, and grasp the abilities of restoring a damaged department. You may get stunned how the power
is definitely with you.
The git push command
To perceive how Git works, we have to take a step again and study how Git shops its knowledge. For Git all the things is about commits. A commit is an object that features a number of keys corresponding to a singular ID, a pointer to the snapshot of the staged content material, and tips to the commits that got here instantly earlier than that commit.
A department, for that matter, is nothing however a pointer to a single commit.
What git push
does is principally:
1. Copies all of the commits that exist within the native department.
2. Integrates the histories by forwarding the distant department to reference the brand new commit, additionally referred to as Fast ahead ref.
Fast ahead ref
Fast ahead is solely forwarding the present commit ref of the department. Git routinely searches for a linear path from the present ref to the goal commit ref while you push your adjustments.
If an ancestor commit exists within the distant and never in native (somebody up to date the distant, and issues have but to replace regionally), Git will not discover a linear path between the commits, and git push
fails.
When to make use of the –force
You can use git rebase
, git squash
, and git commit --amend
to change commit historical past and rewrite beforehand pushed commits. But be warned, my mates, that these mighty instructions do not simply alter the commits—they substitute all commits, creating new ones solely.
A easy git push
fails, and you have to bypass the “fast forward” rule.
Enter --force
.
This choice overrides the “fast forward” restriction and matches our native department to the distant department. The --force
flag permits you to order Git to do it anyway.
When you modify historical past, or while you wish to push adjustments which can be inconsistent with the distant department, you should use push --force
.
Simple state of affairs
Imagine that Lilly and Bob are builders engaged on the identical characteristic department. Lilly accomplished her duties and pushed her adjustments. After some time, Bob additionally completed his work, however earlier than pushing his adjustments, he seen some added adjustments. He carried out a rebase
to maintain the tree clear after which used push --force
to get his adjustments onto the distant. Unfortunately, not being up to date to the distant department, Bob unintentionally erased all of the information of Lilly’s adjustments.
Bob has made a typical mistake when utilizing the --force
choice. Bob forgot to replace (git pull
) his native tracked department. With a department {that a} consumer has but to replace, utilizing --force
brought about Git to push Bob’s adjustments with no regard to the state of the distant tracked department, so commits get misplaced. Of course, you have not misplaced all the things, and the crew can take steps to recuperate, however left uncaught, this error may trigger numerous hassle.
Alternative: push –force-with-lease
The --force
choice has a not-so-famous relative referred to as --force-with-lease
, which lets you push --force
your adjustments with a assure that you just will not overwrite anyone else’s adjustments. By default, --force-with-lease
refuses to replace the department until the remote-tracking department and the distant department factors to the identical commit ref. Pretty nice, proper? It will get higher.
You can specify --force-with-lease
precisely which commit, department, or ref to check to. The --force-with-lease
choice provides you the pliability to override new commits in your distant department whereas defending your previous commit historical past. It’s the identical power however with a life vest.
Guide: How to cope with damaging `–force`
You are, definitely, a accountable developer, however I wager it occurred to you a minimum of as soon as that you just or considered one of your teammates unintentionally ran git push --force
into an essential department that no person ought to ever mess with. In the blink of an eye fixed, all people’s newest work will get misplaced.
No have to panic! If you might be very fortunate, another person engaged on the identical code pulled a latest model of the department simply earlier than you broke it. If so, all you need to do is ask them to --force push
their latest adjustments!
But even if you’re not that fortunate, you might be nonetheless fortunate sufficient to seek out this text.
1. You had been the final particular person to push earlier than the error?
First, DO NOT shut your terminal.
Second, go to your teammates and confess your sins.
Finally, make sure that nobody messes with the repo for the following couple of minutes as a result of you might have some work to do.
Go again to your station. In the output of the git push --force
command in your terminal, search for the road that resembles this one:
+ d02c26f…f00f00ba [branchName] -> [branchName] (pressured replace)
The first group of symbols (which seem like a commit SHA prefix) is the important thing to fixing this.
Suppose your final good decide to the department earlier than you inflicted damages was d02c26f
. Your solely choice is to combat hearth with hearth and push --force
this commit again to the department on prime of the dangerous one:
$ git push — power origin deadbeef:[branchName]
Congratulations! You saved the day!
2. I unintentionally used `–force push` to my repo, and I wish to return to the earlier model. What do I do?
Imagine engaged on a characteristic department. You pulled some adjustments, created a number of commits, accomplished your a part of the characteristic, and pushed your adjustments as much as the primary repository. Then you squashed the commits into one, utilizing git rebase -i
and pushed once more utilizing push --force
. But one thing dangerous occurred, and also you wish to restore your department to the way in which it was earlier than the rebase -i
. The wonderful thing about Git is that it’s the absolute best at by no means dropping knowledge, so the repository model earlier than the rebase
continues to be out there.
In this case, you should use the git reflog
command, which outputs an in depth historical past of the repository.
For each “update” you do in your native repository, Git creates a reference log entry. The git reflog
command outputs these reflogs
, saved in your native Git repository. The output of git reflog
comprises all actions which have modified the information of branches and different references within the native repository, together with switching branches and rebases
. The tip of the department (referred to as HEAD) is a symbolic reference to the at present energetic department. It’s solely a symbolic reference since a department is a pointer to a commit.
Here is a straightforward reflog
that reveals the state of affairs I described above:
1b46bfc65e (HEAD -> test-branch) HEAD @ {0} : rebase -i (end): returning to refs/heads/test-branch
b46bfc65e (HEAD -> test-branch) HEAD @ {1}: rebase -i (squash): a
dd7906a87 HEAD @ {2} : rebase -i (squash): # This is a mix of two commits.
a3030290a HEADC {3}: rebase -i (begin): checkout refs/heads/grasp
Oc2d866ab HEAD@{4}: commit: c
6cab968c7 HEAD@ {5} : commit: b
a3030290a HEAD @ {6}: commit: a
c9c495792 (origin/grasp, origin/HEAD, grasp) HEAD@ {7}: checkout: shifting from grasp to test-branch
c9c495792 (origin/grasp, origin/HEAD, grasp) HEAD@ {8} : pull: Fast-forward
The notation HEAD@{quantity}
is the place of HEAD at quantity
of adjustments in the past. So HEAD@{0}
is the HEAD the place HEAD is now and HEAD@{4}
is HEAD 4 steps in the past. You can see from the reflog
above that HEAD@{4}
is the place it’s essential go to revive the department to the place it was earlier than the rebase, and 0c2d866ab
is the commit ID for that commit.
So to revive the check department to the state you need, you reset the department:
$ git reset — exhausting HEAD@{4}
Then you may power push once more to revive the repository to the place it was earlier than.
General restoration
Anytime you wish to restore your department to the earlier model after you push --force
, observe this basic restoration answer template:
1. Get the earlier commit utilizing the terminal.
2. Create a department or reset to the earlier commit.
3. Use push --force
.
If you created a brand new department, do not forget to reset the department, so it is synced with the distant by operating the next command:
$ git reset --hard origin/[new-branch-name]
3. Restore push –force deleted department with git fsck
Suppose you personal a repository.
You had a developer who wrote the challenge for you.
The developer determined to delete all of the branches and push --force
a commit with the message “The project was here.”
The developer left the nation with no approach to contact or discover them. You’ve received no code, and you have by no means cloned the repo.
First factor first—it’s essential discover a earlier commit.
Sadly, on this case, utilizing git log
will not assist as a result of the one commit the department factors to is “The project was here” with none associated commits. In this case, you need to discover deleted commits that no youngster commit, department, tag, or different reference had linked to. Fortunately, the Git database shops these orphan commits, and you will discover them utilizing the highly effective git fsck
command.
git fsck --lost-found
They name these commits “dangling commits.” According to the docs, easy git gc
removes dangling commits two weeks previous. In this case, all you might have are dangling commits, and all you might have left to do is use the one earlier commit from earlier than the damages and observe the overall restoration steps above.
Protected branches and code opinions
As the previous saying goes:
“The difference between a smart person and a clever person is that a smart person knows how to get out of trouble that a clever person wouldn’t have gotten into in the first place.”
If you want to fully keep away from push --force
, each GitHub and GitLab provide a really cool characteristic referred to as Protected Branches, which lets you mark any department as protected so nobody can push --force
to it.
You also can set admin preferences to limit permissions.
Alternatively, you may institute Git hooks to require code opinions or approval earlier than anybody can push code to an essential department.
Summary
Hopefully, you now perceive when it’s essential add the --force
choice and the dangers concerned when utilizing it. Remember, the --force
is there for you. It’s solely a bypass, and like each bypass, it’s best to use it with care.
May the --force
be with you.
This article is customized from the creator’s Medium article and is republished with permission.