Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [egit-dev] Missing tree/blob <hash> when trying to push

Hi,

finally I found cause for the push problem in the repo's you sent. I
am not sure whether this is an explanation for every use case where
you see the problem, but at least one problem visible in your repos is
explainable by that.

What it boils down to is: JGit was changed in a incompatible way. A
JGit client >= 3.5.0 may cause (under rare conditions) problems when
pushing to a server based on JGit<3.5.0. The solution is easy: Upgrade
your servers to be based on JGit>=3.5.0. Or, alternatively, downgrade
your clients to be based on <3.5.0.

I am still not sure if we have to do something about that in JGit. One
possible solution would be to let the client find out whether the
server is older than 3.5.0 and to use old code in that case. But I
don't know how a client could find that out.

If anybody has problems with push where a upgrade of the server
doesn't help then that's a different problem. Does anybody see such a
case?

Here is the technical explanation:
========================

How a push should work:
Imagine a client wants to push a bunch of refs to the server. X is the
set of commits these refs point to. During push the server tells the
client which refs he knows of. Y is the set of commits these refs are
pointing to. Now the client determines the set of objects (not only
the commits but also the trees,blobs,tags) which are referenced
recursively by X. Lets call it X*. And he determines the set of
objects known to the server Y*. And he sends the objects X*-Y* to the
server.
On the server side a connectivity check is done whenever new objects
are received. Imagine he server has received the objects Z. He makes
sure that every object referenced by Z is either contained in Z or
already known to him. Means: he computes Z*-Y*. He makes sure
(Z*-Y*)-Z is empty. If not he throws the "Missing*" Exception
described in this mail.

Example:
1) Client: I want to push refs/heads/master pointing to commit 5
(X={5}) to the server. The full set of objects referenced by 5 is
X*={1,2,3,4,5}.
2) Server: I know the refs refs/heads/master->1 and refs/heads/side->3 (Y={1,3})
3) Client: Computes Y*={1,2,3}. He sends a packfile containing
Z=X*-Y*={4,5} to the server
4) Server: Receives Z={4,5}. He computes Z*-Y* (={4,5}). He makes sure
that (Z*-Y*)-Z is empty. {4,5}-{4,5}={} -> Success

Behaviour bevor JGit 3.5.0 (more specific: commit c4797fe) on client
and server side:
X*-Y* (and Z*-Y*) is computed by an ObjectWalk. X are marked as
starting points and Y are marked as uninteresting in this walk. Before
JGit 3.5.0 there was a bug in ObjectWalk so that sometimes the result
contained more objects than it should.
1) Client: I want to push refs/heads/master pointing to commit 5
(X={5}) to the server. The full set of objects referenced by 5 is
X*={1,2,3,4,5}.
2) Server: I know the refs refs/heads/master->1 and refs/heads/side->3 (Y={1,3})
3) Client: Computes Y*={1,2,3}. He sends a packfile containing
Z=X*-Y*={3,4,5} (BUG!) to the server
4) Server: Receives Z={3,4,5}. He computes Z*-Y* (={3,4,5}) (BUG!). He
makes sure that (Z*-Y*)-Z is empty. {3,4,5}-{3,4,5}={} -> Success

Behaviour with JGit <3.5.0 as server and JGit >=3.5.0 as client
1) Client: I want to push refs/heads/master pointing to commit 5
(X={5}) to the server. The full set of objects referenced by 5 is
X*={1,2,3,4,5}.
2) Server: I know the refs refs/heads/master->1 and refs/heads/side->3 (Y={1,3})
3) Client: Computes Y*={1,2,3}. He sends a packfile containing
Z=X*-Y*={4,5} to the server
4) Server: Receives Z={4,5}. He computes Z*-Y* (={3,4,5}) (BUG!). He
makes sure that (Z*-Y*)-Z is empty. {3,4,5}-{4,5}={3} --> EXCEPTION

Behaviour with JGit >=3.5.0 as server and JGit<3.5.0 as client
1) Client: I want to push refs/heads/master pointing to commit 5
(X={5}) to the server. The full set of objects referenced by 5 is
X*={1,2,3,4,5}.
2) Server: I know the refs refs/heads/master->1 and refs/heads/side->3 (Y={1,3})
3) Client: Computes Y*={1,2,3}. He sends a packfile containing
Z=X*-Y*={3,4,5} (BUG!) to the server
4) Server: Receives Z={3,4,5}. He computes Z*-Y* (={4,5}). He makes
sure that (Z*-Y*)-Z is empty. {4,5}-{3,4,5}={} -> Success

Ciao
  Chris


Back to the top