1

what exactly happens when you git push with --force?

I have been searching the web for about an hour now, and the most relevant answer I can find is:

Same as [git push], but force the push even if it results in a non-fast-forward merge. Do not use the --force flag unless you’re absolutely sure you know what you’re doing.

-source from https://www.atlassian.com/git/tutorial/remote-repositories#!push

I understand this well enough, I think, but to me this is like saying git push --force will force a push. Despite my searching, I can't find anything really concrete that outlines the process involved in a force push.

Hypothetically, I have a main repo set up called git@heroku.com:my-app.git, and a mirror of that repo set up on Heroku for staging called git@heroku.com:my-app-staging.git.

I have created a local branch called 'new_changes', done work, and pushed to staging from this branch.

I didn't like the results, so I abandoned the project, set up a new branch called 'more_new_changes', did some work, tested it locally, merged with master, and tried to push to staging, just to make sure staging is up-to-date.

My push tells me...

Pushing to git@heroku.com:my-app-staging.git
Fetching repository, done.
To git@heroku.com:my-app-staging.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@heroku.com:my-app-staging.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

I don't want to merge the code that's on staging into my local master branch- in fact, I want that staging code to be gone. (Obviously, the branches would be titled with hashes, but for the sake of argument) I figure that if my staging looks like

push079->push080->new_changes(HEAD)

and my local looks like

push079->push080->more_new_changes(HEAD)

and I decide to push to staging using --force, then the result would be a staging branch that looks like

push079->push080->more_new_changes(HEAD)

Alternatively it could look more like

push079->push080->new_changes
                                     \
                                       >more_new_changes(HEAD)

but if I'm wrong, and it just squashes the two together, and I wind up with something like

push079->push080->new_changes->more_new_changes(HEAD)

then I lose the continuity of having a staging repo that mirror-reflects my production repo.

So, what actually happens behind the scenes when you push using --force?

Crash
  • 321
  • 1
  • 4
  • 15
  • possible duplicate of [Other consequences of \`git push --force\`?](http://stackoverflow.com/questions/21259585/other-consequences-of-git-push-force) – Joe May 03 '14 at 10:39
  • @Joe I disagree- while the link is appreciated, that question you reference is asking what harm --force does. I'm not asking about negative effects- I already have a good grasp on that; it is not pertinent to me as nobody on my dev team would ever pull from staging- it's simply a push-only environment used for tests. I'm not concerned about negative effects of forcing a push into staging. What I want to know is more about core functionality. I want to know if using force will overwrite unwanted code like I hope it does. Same topic, completely different question. – Crash May 05 '14 at 14:24
  • It's perhaps not an exact match. However, a key part ("Maybe it causes some extra garbage in the remote") and the answer ("A force-push merely tells the remote to move the given label(s) even if the move is not a fast-forward operation.") seem to cover your question. – Joe May 06 '14 at 12:28
  • Not very "behind the scenes" though. It doesn't tell you a lot- in what way are the labels moved? What are the actions taken, and what outcome do the actions have? With that answer, in order to get the answer I'm really looking for, I still have to search elsewhere. It's a good answer for that question, but in no way the answer to mine. I think a mix of both answers posted is more-or-less what I'm looking for. – Crash May 06 '14 at 16:38

2 Answers2

1

It's extremely simple: the Git client sends a message that amounts to "make ref such-and-such point to this commit". Server and client exchange information until the server has the required commit and its history. The server then updates the ref, i.e. writes the SHA1 to a text file.

What happens in your case is that the server refuses the force-push because it doesn't want to lose any history.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • So, you would need to get rid of the previous commit before pushing the new one? – Crash May 02 '14 at 17:24
  • @Crash `push --force` gets rid of whatever was on the server (the next time it runs a garbage collection, that is). – Fred Foo May 02 '14 at 20:25
  • @larsmans- But like VonC said, you can run fsck on it- I'm guessing that only applies before garbage collection? – Crash May 02 '14 at 22:30
  • @Crash: yes. But typically, you can't git fsck remotely, you need shell access for that. – Fred Foo May 03 '14 at 09:25
1

The result of a git push --force will actually be:

push079->push080->more_new_changes(HEAD)
             \
              ->new_changes (reflog only or git fsck)

One commit will replace the other: you don't need to get rid of the previous commit before pushing the new one.

This answer illustrates how git fsck would allow you to get back the history you would replace with a git push --force.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250