Suppose I work on a personal branch called feature
.
git reset --hard
back to commit 3.-f
).Is that safe to do or that will cause any issues with master? I will need to merge feature
into master
eventually.
Force pushing can be dangerous, as you could remove code from your remote repository that you don't have locally. For example, if another developer pushed code to the remote branch that you don't have locally, that code could be lost.
The --force option for git push allows you to override this rule: the commit history on the remote will be forcefully overwritten with your own local history. This is a rather dangerous process, because it's very easy to overwrite (and thereby lose) commits from your colleagues.
The Risks of Git Push ForceBecause you have failed to pull those changes, they are not reflected in your local repository. In this case, if you perform a Git push force, you will replace the remote repository with a copy of your local repo, effectively deleting your team member's work.
Actually, force pushing to a feature branch on which only you work is not bad practice at all. --force-with-lease is safer, not safe - it's still not generally a good thing to do on a shared branch.
You're safe enough, as long as it really is private, and you only push to the private branch; here's why.
A branch is a specific kind of "reference", giving the ID of a commit.
When you're pushing or fetching, you are asking your git to use your repo to talk with another git and another repo. With a fetch
you ask your git to ask their git to give your git things you don't have yet, and then your git sets up "remote branches" for your repo, identifying new stuff brought over. With a push
you ask your git to send over some commits, then ask their git to make their reference(s) point to (some of) those commit(s).
When you "force push" you're making your git tell their git to set those references, even if that might "lose" commits (a la git reset
).
Remember, references make commits reachable. In git, commits form a graph (a "directed acyclic" graph or DAG):
A <- B <- C
Here each single uppercase letter refers to a commit. Each commit has a different "true name" SHA-1 (40 character apparent-nonsense like 1fc3e7aa...
). Given one of these big long ugly SHA-1 names, like the true name for commit C
, you (or git) read the commit and find the "parent" commit, in this case a different big ugly SHA-1 for commit B
. You then read that commit and find the big ugly SHA-1 for commit A
.
But where do you get the big ugly SHA-1 for commit C
? You could try to memorize it, but that seems like a bad plan. Instead, why not create a little file, like .git/refs/heads/feature
, and write the big ugly SHA-1 into that file? Even better, how about having git do that?
That's what a reference is—in this case, a branch name: a name under the refs/heads/
category.
Now all you have to do is remember the name feature
. Moreover, git can look in the refs/
directory and find all your references: branches (in refs/heads/
), tags (in refs/tags/
), notes (in refs/notes/
), and so on.
Any commit that has a name pointing to it is, obviously, "reachable": you (or git) open the name, read the SHA-1, and go get the commit. But any commit that is indirectly find-able this way is also reachable: that is, as long as we can find C
directly, we can use it to find B
, and then use that to find A
.
When you do a git reset
, you are telling your git to move a reference. Let's extend that sequence a bit to add a new commit D
, and make feature
point to D
:
A <- B <- C <- D <-- feature
Now let's git reset --hard
to commit C
. I can't really grey-out anything in plain text but I'll shove D
up out of the way:
D
/
A <- B <- C <-- feature
We've told git to move feature
to point to C
. What points to D
? Nothing—well, "almost nothing": git has "reflogs" and there are reflog entries keeping D
alive for another 30 days (by default anyway). But for normal purposes, commit D
is gone. It's no longer easily reachable, at least, and git log
won't show it by default. Once it's truly unreachable (no more reflog entries), git will eventually "garbage collect" it and delete it from the repository.
If you now ask git push
to tell some other git to set some other repository's feature
to point to commit C
, that will lose commit D
from there too (assuming, of course, that they had D
in the first place).
Where things go particularly bad here is when someone else, someone using this same "other" (shared) git repo, is using—and depending-on—commit D
. At some point, he'll call up this shared repo and ask what's in feature
and get back a reply saying "branch feature
points to commit C
" and he'll then have to figure out how to do without commit D
, or re-provide commit D
, or whatever.
You said no one else is using this branch (it's private), so no one else is depending on commit D
.
One other thing to know is that while your reflogs will let you revive commit D
for 30 more days, a repository that you can push-to often has reflogs disabled. This means that as soon as you make commit D
unreachable on the server, it's likely to get garbage-collected.
Last, if you start getting into a habit of force-pushing, be very careful what you force-push, lest you accidentally force-push-delete commits that other people are using/depending-on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With