32

I recently received a "forced update" warning from git on a repository which only I commit to. I haven't done any re-basing so I don't know why this happened. What I want to know is, where should I look to find changes that have potentially been lost?

To illustrate, let there be three copies of the repository, L, D and S (laptop, desktop, server).

To begin all three repositories are in-sync. Then work is done on D and pushed to S. Then L runs git pull and gets "forced update". Does this mean that there are changes made on L that have been overwritten, or are they somewhere else? How can I find them? Thanks.

Sean Whitton
  • 505
  • 1
  • 4
  • 6

3 Answers3

26

A "forced update" means the remote-tracking branch was recent. This happens if you fetch (or pull) after someone does a force push to the repository.

However, when executing the git pull, your local branch won't lose any history. Since the remote branch's history now diverges from your local, git pull will execute a merge. If you look at the most recent commit (just run git log) you should see a merge commit, with the first parent being the previous state of your local branch and the second parent being the new value of your remote branch.

For illustration, I just reproduced the forced update scenario, and a git pull prints the following:

> git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
From /Volumes/UserData/Users/kballard/Dev/Scratch/foo/server
 + 7193788...a978889 master     -> origin/master  (forced update)
Merge made by the 'recursive' strategy.
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d

The fetch portion of the pull prints (forced update), but the new value of origin/master is subsequently merged into the local branch.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 2
    Thanks for the reply. However, I did not see anything like your output, I got this: + 3c4a314...e58bcd9 git-annex -> origin/git-annex (forced update) + 805d6cc...8fc53c5 master -> origin/master (forced update) Already up-to-date. Why was there no merge? – Sean Whitton Aug 22 '12 at 09:37
  • 1
    @SeanWhitton; "Already up-to-date" means your local branch already contained the remote branch. It sounds like someone force-pushed the remote branch to backdate it, but didn't add any new commits. You can check `git log origin/master..master` to see what commits you have locally that aren't on the remote. – Lily Ballard Aug 22 '12 at 20:44
  • `git log origin/master..master` gives nothing, so I'm safe from data lost. Since I am the only committer and know for sure I didn't force push, I still have no idea why this happened. Thanks again for the explanation. – Sean Whitton Aug 23 '12 at 08:23
  • @SeanWhitton: Any chance you accidentally modified your remote-tracking branch `(origin/master)` without modifying the actual remote? Because that would produce the same effects, with the next fetch/pull resetting the remote tracking branch back to the state of the remote. – Lily Ballard Aug 23 '12 at 19:21
  • Possible, but seems unlikely because both the master and git-annex branches had a forced update at the same time and it seems unlikely that I'd accidentally modify both of them. It may have been a bug in git-annex. – Sean Whitton Aug 23 '12 at 20:41
  • Do you mean "resent", not "recent", in your first sentence? – Andrew Grimm Mar 10 '17 at 07:47
  • 4
    No I think I probably meant "reset". But this answer is 4.5 years old so I can't say with any certainty what was going through my mind when I wrote that. – Lily Ballard Mar 11 '17 at 05:55
7

What I want to know is, where should I look to find changes that have potentially been lost?

Kevin is right when writing that no local history will be lost. Still, some remote history may be lost, though it will be lost on purpose.

Example

For example:

  • D commits and pushes master to S.
  • L fetches.
  • D changes mind (amends a commit, or changes history of master in whatever way).
  • D pushes the modified (non-fast-forward) master to S.

On fetch L will warn "forced update" because the previous branch tip is no longer reachable through the branch reference master. But that's what D wanted anyway.

What to look for

From Kevin's answer:

 + 7193788...a978889 master     -> origin/master  (forced update)

That line will appear only once. The reference to the possibly lost commit is 7193788.

How to explore it

If L wants to keep a reference to what might be lost, L might in above example issue: git branch whateverbranchname 7193788. This may be done whatever the current state of the local checkout.

Or just git checkout 7193788 to explore it in detached head, then e.g. git checkout master to get back to master. This may require to first commit any local change.

Good practice

Notice that it's considered bad practice to push a changed history on a shared repository without proper cooperation with other users (because it causes extra work for people who have changes not yet shared).

In other words, no one should be surprised to see a "forced update" when doing a fetch on a shared repository. If someone pushed a bad commit, they should consider just pushing a corrected commit on top, not change existing history. Alternatively, they should get into agreement with others before pushing a changed history. That latter option is not possible on a public repository.

Stéphane Gourichon
  • 6,493
  • 4
  • 37
  • 48
1

Git 2.23 (Q3 2019) illustrates when a "forced update" is detected, and propose the option to not show it.

See commit 3883c55, commit 377444b, commit cdbd70c (18 Jun 2019) by Derrick Stolee (derrickstolee).
(Merged by Junio C Hamano -- gitster -- in commit cde9a64, 09 Jul 2019)

fetch: add --[no-]show-forced-updates argument

After updating a set of remove refs during a 'git fetch', we walk the commits in the new ref value and not in the old ref value to discover if the update was a forced update.

This results in two things happening during the command:

  1. The line including the ref update has an additional "(forced-update)" marker at the end.

  2. The ref log for that remote branch includes a bit saying that update is a forced update.

For many situations, this forced-update message happens infrequently, or is a small bit of information among many ref updates.
Many users ignore these messages, but the calculation required here slows down their fetches significantly.
Keep in mind that they do not have the opportunity to calculate a commit-graph file containing the newly-fetched commits, so these comparisons can be very slow.

Add a '--[no-]show-forced-updates' option that allows a user to skip this calculation.
The only permanent result is dropping the forced-update bit in the reflog.

Include a new fetch.showForcedUpdates config setting that allows this behavior without including the argument in every command.
The config setting is overridden by the command-line arguments.

That means the documentation now has:

fetch.showForcedUpdates:

Set to false to enable --no-show-forced-updates in git-fetch and git-pull commands.
Defaults to true.

--show-forced-updates:

By default, Git checks if a branch is force-updated during fetch.
This can be disabled through fetch.showForcedUpdates, but the --show-forced-updates option guarantees this check occurs.

--no-show-forced-updates:

By default, Git checks if a branch is force-updated during fetch.
Pass --no-show-forced-updates or set fetch.showForcedUpdates to false to skip this check for performance reasons.

If used during 'git-pull' the --ff-only option will still check for forced updates before attempting a fast-forward update.

And:

fetch: warn about forced updates in branch listing

The --[no-]show-forced-updates option in 'git fetch' can be confusing for some users, especially if it is enabled via config setting and not by argument.
Add advice to warn the user that the (forced update) messages were not listed.

Additionally, warn users when the forced update check takes longer than ten seconds, and recommend that they disable the check.
These messages can be disabled by the advice.fetchShowForcedUpdates config setting.

The config advice documentation now includes:

fetchShowForcedUpdates:

Advice shown when git-fetch takes a long time to calculate forced updates after ref updates, or to warn that the check is disabled.

Note, the test is fixed in Git 2.3.1/2.24 (Q4 2019)

See commit 814291c (30 Jul 2019) by SZEDER Gábor (szeder).
(Merged by Junio C Hamano -- gitster -- in commit 8aa76ab, 22 Aug 2019)

See commit decfe05, commit 7f005b0, commit 12b1826 (01 Aug 2019) by SZEDER Gábor (szeder).
(Merged by Junio C Hamano -- gitster -- in commit 77067b6, 22 Aug 2019)

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