You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Ben Collins-Sussman <su...@collab.net> on 2001/07/11 19:34:28 UTC

the GHP

                    The "Greg Hudson" Problems
                               or,
              Why versioning directories is Really Hard
              -----------------------------------------

(Greg, please pardon the name.  You were the first to point out these
philosophical problems, so we've started using your name to refer to
them.  :-) )


PROBLEM 1 : the lagging parent directory
---------

Assume my working copy has two directories both at revision 3:

       A/D    : 3
       A/D/H  : 3

Now I `svn rm A/D/H` and commit, which produces revision 6 in the
repository.   My working copy now looks like:

       A/D    : 3

A/D/H is gone, because the RA layer told us it was a target that was
successfully committed.  The client routine log_do_committed() got
this message, saw that the A/D/H entry was originally scheduled to be
deleted, and so it took the liberty of actually removing it during
commit cleanup.

Now, when I `svn up` to revision 6, the client sends an incomplete
report to the server on the working copy state:

       A/D    : 3

It's incomplete, because there's no mention of A/D/H anywhere.  As a
result, the repository *assumes* that the client still has A/D/H:3,
and thus tells the client to remove A/D/H.  This is an error -- A/D/H
is already gone.

(Incidentally, this bug would still be the same if H were a file,
instead of a dir.) 



PROBLEM 2 : the incomplete directory
---------

Assume my working copy has a directory, everything at revision 3:

       A/D       : 3
       A/D/H     : 3
       A/D/foo.c : 3

Now, unbeknownst to me, somebody else adds a new file `bar.c' to this
directory, creating revision 4.

Then I add a property to A/D and commit.  The RA layer replies that
A/D was successfully committed, creating revision 5, so
log_do_committed() bumps A/D's revision to 5 in my working copy.

Of course, this is completely bogus.  My working copy does *not* have
revision 5 of A/D -- it has no idea that `bar.c' exists.




SOLUTION to Problem 1  (in progress)
--------

A subversion entry has more than just a "schedule" flag.  It now has
an "existence" flag, used specifically to describe an entry that is
out-of-sync with its parent's revision.


>> Adding a "deleted" flag

Thus, when log_do_committed() is told that A/D/H was committed, and
sees A/D/H was slated for deletion, it examines revision numbers.  If
the committed target's revision (6) is newer than the parent's
revision (3), it marks the entry with "existence = deleted."  (If the
two revision numbers are the same, then the entry is removed instead.)


>> Make state-reports smarter

svn_wc_crawl_revisions now notices the "deleted" flag and gives an
accurate working-copy report to the repository:

      A/D    : 3
      A/D/H  : missing

And now the repository will *not* tell the client to remove A/D/H.


>> Removing the "deleted" flag

There are three scenarios whereby the "deleted" flag goes away:

  * updating parent to a revision where the entry exists

     Assume that A/D/H is re-added in revision 10, and we're updating
     to this revision.  Or assume we're updating to revision 4, where
     the removal hasn't happened yet.

     The repository will tell the update-editor to re-add H.  The
     update-editor notices that H already exists -- but instead of
     producing an "obstructed update" error, it sees the "deleted"
     flag and instead removes and re-writes the whole entry.

  * updating parent to a revision where the entry has been deleted

     After an update finishes, ensure_uniform_revision() sets all
     working-copy revisions to the same value.  It notices a "deleted"
     entry, and if it's own revision is less than or equal to the new
     uniform revision, it can safely remove the entry.  The parent has
     "caught up" with the child.

  * committing parent 

     If the parent is committed, log_do_committed() will be informed
     that it was successfully committed to a new revision number.
     (This new revision number *must* be higher than 6, the
     deleted-child's revision!)    

     So log_do_committed() will notice the parent's "deleted" child at
     revision 6, which is younger, and remove the entry altogether.



SOLUTION to Problem 2
--------

Still thinking on this one.  Yikes.



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: the GHP

Posted by Karl Fogel <kf...@collab.net>.
Ben Collins-Sussman <su...@collab.net> writes:
> PROBLEM 1 : the lagging parent directory
> ---------
> 
> Assume my working copy has two directories both at revision 3:
> 
>        A/D    : 3
>        A/D/H  : 3
> 
> Now I `svn rm A/D/H` and commit, which produces revision 6 in the
> repository.   My working copy now looks like:
> 
>        A/D    : 3
> 
> A/D/H is gone, because the RA layer told us it was a target that was
> successfully committed.  The client routine log_do_committed() got
> this message, saw that the A/D/H entry was originally scheduled to be
> deleted, and so it took the liberty of actually removing it during
> commit cleanup.
> 
> Now, when I `svn up` to revision 6, the client sends an incomplete
> report to the server on the working copy state:
> 
>        A/D    : 3
> 
> It's incomplete, because there's no mention of A/D/H anywhere.  As a
> result, the repository *assumes* that the client still has A/D/H:3,
> and thus tells the client to remove A/D/H.  This is an error -- A/D/H
> is already gone.
> 
> (Incidentally, this bug would still be the same if H were a file,
> instead of a dir.) 

This problem has actually been discussed and (I believe) solved
already:

After committing the deletion of A/D/H:3 (thus producing revision 6 in
the repository), the working copy must record that H is at revision 6
-- a revision in which H does not exist, but nonetheless it's a
perfectly valid thing to say.  In other words, in A/D/SVN/entries, the
entry for H will still be present, and will claim the entry is at
revision 6, and there should also be a flag indicating that in that
revision, said entry does not exist.  This flag will remain present
until the parent directory, A/D, is updated to revision 6 or beyond.

Oh.  And now I see that this is exactly what you wrote. :-)  There
were a few newlines separating the problems from the solutions, so I
didn't realize there was more to the mail until after I'd written the
above.

Moving on to Problem 2:

> PROBLEM 2 : the incomplete directory
> ---------
> 
> Assume my working copy has a directory, everything at revision 3:
> 
>        A/D       : 3
>        A/D/H     : 3
>        A/D/foo.c : 3
> 
> Now, unbeknownst to me, somebody else adds a new file `bar.c' to this
> directory, creating revision 4.
> 
> Then I add a property to A/D and commit.  The RA layer replies that
> A/D was successfully committed, creating revision 5, so
> log_do_committed() bumps A/D's revision to 5 in my working copy.
> 
> Of course, this is completely bogus.  My working copy does *not* have
> revision 5 of A/D -- it has no idea that `bar.c' exists.

I thought we decided that for changes to the directory _itself_ (i.e.,
property changes), the dir would need to be up-to-date, since such
changes are not auto-mergeable.  (Yes, in fact this change *could* be
automerged with a later revision of the dir if that later revision
differs only in entries, not in properties, but if our rule at commit
time is simply "Don't commit changes to dir properties when the
directory is not completely up-to-date" then we're always safe.)

-Karl



> 
> 
> 
> 
> SOLUTION to Problem 1  (in progress)
> --------
> 
> A subversion entry has more than just a "schedule" flag.  It now has
> an "existence" flag, used specifically to describe an entry that is
> out-of-sync with its parent's revision.
> 
> 
> >> Adding a "deleted" flag
> 
> Thus, when log_do_committed() is told that A/D/H was committed, and
> sees A/D/H was slated for deletion, it examines revision numbers.  If
> the committed target's revision (6) is newer than the parent's
> revision (3), it marks the entry with "existence = deleted."  (If the
> two revision numbers are the same, then the entry is removed instead.)
> 
> 
> >> Make state-reports smarter
> 
> svn_wc_crawl_revisions now notices the "deleted" flag and gives an
> accurate working-copy report to the repository:
> 
>       A/D    : 3
>       A/D/H  : missing
> 
> And now the repository will *not* tell the client to remove A/D/H.
> 
> 
> >> Removing the "deleted" flag
> 
> There are three scenarios whereby the "deleted" flag goes away:
> 
>   * updating parent to a revision where the entry exists
> 
>      Assume that A/D/H is re-added in revision 10, and we're updating
>      to this revision.  Or assume we're updating to revision 4, where
>      the removal hasn't happened yet.
> 
>      The repository will tell the update-editor to re-add H.  The
>      update-editor notices that H already exists -- but instead of
>      producing an "obstructed update" error, it sees the "deleted"
>      flag and instead removes and re-writes the whole entry.
> 
>   * updating parent to a revision where the entry has been deleted
> 
>      After an update finishes, ensure_uniform_revision() sets all
>      working-copy revisions to the same value.  It notices a "deleted"
>      entry, and if it's own revision is less than or equal to the new
>      uniform revision, it can safely remove the entry.  The parent has
>      "caught up" with the child.
> 
>   * committing parent 
> 
>      If the parent is committed, log_do_committed() will be informed
>      that it was successfully committed to a new revision number.
>      (This new revision number *must* be higher than 6, the
>      deleted-child's revision!)    
> 
>      So log_do_committed() will notice the parent's "deleted" child at
>      revision 6, which is younger, and remove the entry altogether.
> 
> 
> 
> SOLUTION to Problem 2
> --------
> 
> Still thinking on this one.  Yikes.
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: dev-help@subversion.tigris.org

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org