Layered Moves

The idea is to store moved_to in the presence=base-deleted node, rather than the BASE node. If/when the node is replaced and becomes presence=normal the moved_to still refers to the node that was base-deleted.

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

 

table A1

 

 

Move A/B/C/D to X

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

4

A/B/C/D

base-deleted

X

 

table A2

 

 

Move A/B to Y

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

2

A/B

base-deleted

Y

2

A/B/C

base-deleted

 

2

A/B/C/D

base-deleted

X

 

table A3

 

 

Replace A/B with a copy that includes a C/D

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

2

A/B

normal

Y

2

A/B/C

normal

 

2

A/B/C/D

normal

X

 

table A4

 

 

Move A/B/C to Z

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

2

A/B

normal

Y

2

A/B/C

normal

 

2

A/B/C/D

normal

X

3

A/B/C

base-deleted

Z

3

A/B/C/D

base-deleted

 

 

table A5

 

 

Replace A/B/C with a copy that includes D

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

2

A/B

normal

Y

2

A/B/C

normal

 

2

A/B/C/D

normal

X

3

A/B/C

normal

Z

3

A/B/C/D

normal

 

 

table A6

 

 

Move A/B/C/D to Q

op-depth

local_relpath

presence

moved_to

0

A

normal

 

0

A/B

normal

 

0

A/B/C

normal

 

0

A/B/C/D

normal

 

2

A/B

normal

Y

2

A/B/C

normal

 

2

A/B/C/D

normal

X

3

A/B/C

normal

Z

3

A/B/C/D

normal

 

4

A/B/C/D

base-deleted

Q

 

table A7

 

 

Now scan_deletion(A/B/C/D) needs to tell us moved_to is Q at op-depth=4, Z/D at op-depth=3, X at op-depth=2. Quite how it does this is an open question. Does the caller pass the op-depth? Where would the caller get it? Does the function return some sort of array of (op-depth, moved-to) pairs?

Nested Moves

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

 

table B1

 

 

Move A to B and then B/F to B/G

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

A

base-deleted

B

1

A/F

base-deleted

 

1

B

normal

 

1

B/F

normal

 

 

table B2

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

A

base-deleted

B

1

A/F

base-deleted

 

1

B

normal

 

1

B/F

normal

 

2

B/F

base-deleted

B/G

2

B/G

normal

 

 

table B3

 

 

Alternatively, move A/F to A/G and then A to B

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

2

A/F

base-deleted

A/G

2

A/G

normal

 

 

table B4

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

A

base-deleted

B

1

A/F

base-deleted

 

1

B

normal

 

1

B/F

normal

 

2

B/F

base-deleted

B/G

2

B/G

normal

 

 

table B5

 

 

The final database state is the same independent of the order of the moves. The second move in the second case causes the explicit moved-to associated with A/F to be removed.

Copy compared to Move

Now consider copying A instead of moving it, so copy A to C then move C/F to C/G.

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

C

normal

 

1

C/F

normal

 

2

C/F

base-deleted

C/G

2

C/G

normal

 

 

table C1

 

 

There is only one move recorded C/F->C/G. The move inside the copy is recorded in a similar way to the move inside a move. The move can happen before the copy:

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

2

A/F

base-deleted

A/G

2

A/G

normal

 

 

table C2

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

2

A/F

base-deleted

A/G

2

A/G

normal

 

1

C

normal

 

1

C/F

normal

 

2

C/F

base-deleted

C/G

2

C/G

normal

 

 

table C3

 

 

Irrespective of the order of the copy and move operations we end up with moved-to for C/F to C/G.

Move rewrites moved-to

Move has to rewrite moved-to both inside an outside the moved tree. The first case is moved-to entirely within the moved tree: move A to B after A/F to A/G. This causes moved-to on A/F to be rewritted (to null) and moved-to on B/F to be added:

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

2

A/F

base-deleted

A/G

2

A/G

normal

 

 

table D1

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

A

base-deleted

B

1

A/F

base-deleted

 

1

B

normal

 

1

B/F

normal

 

2

B/F

base-deleted

B/G

2

B/G

normal

 

 

table D2

 

 

The second case is moved-to from inside the moved-tree to outside the moved tree: move A to B after A/F to G. Like the first case this requires the moved-to on A/F to be rewritten (to null) and moved-to on B/F to be added. Compared to the first case the value of the rewritten moved-to doesn't change:

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

2

A/F

base-deleted

G

1

G

normal

 

 

table D3

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

A/F

normal

 

1

A

base-deleted

B

1

A/F

base-deleted

 

1

B

normal

 

1

B/F

normal

 

2

B/F

base-deleted

G

1

G

normal

 

 

table D4

 

 

The third case is move-to from outside the moved tree into the moved tree: move A to B after F to A/F. This causes moved-to on F, outside the moved tree, to be rewritten:

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

F

normal

 

1

F

base-deleted

A/G

2

A/G

normal

 

 

table D5

 

 

op-depth

local-relpath

presence

moved-to

0

A

normal

 

0

F

normal

 

1

A

base-deleted

B

1

F

base-deleted

B/G

1

B

normal

 

2

B/G

normal

 

 

table D6

 

 

No Mixed-Revision Moves

Subversion does support mixed-revision copies but moves will be restricted to single-revision trees. This doesn't restrict users in practice since these are the only sort of moves that can be committed. A mixed revision copy:

op-depth

local-relpath

presence

revision

0

A

normal

4

0

A/B

normal

6

1

X

normal

4

1

X/B

not-present

 

2

X/B

normal

6

 

table E1

 

 

and two nested copies:

op-depth

local-relpath

presence

revision

0

A

normal

4

0

A/B

normal

6

1

X

normal

4

1

X/B

normal

4

2

X/B

normal

6

 

table E2

 

 

both get committed as two copies:

A /X (from /A:4)

A /X/B (from /A/B:6)

If the revisions later than r4 do not affect the /A tree then we can also commit the delete of /A but the result doesn't look like a mixed-revision move:

D /A

A /X (from /A:4)

A /X/B (from /A/B:6)

This is a move of /A with an additional copy inside the move destination. Moves are essentially single revision in the repository: the same node gets deleted and copied and the deleted node has to be single-revision.

It will be possible to move single-revision trees with local modifications, e.g. a tree A with a locally added A/G:

op-depth

local-relpath

presence

moved-to

moved-here

0

A

normal

 

 

0

A/F

normal

 

 

2

A/G

normal

 

 

 

 

table E3

 

 

Move A to B gives a single-revision move of A-to-B with a locally added B/G:

op-depth

local-relpath

presence

moved-to

moved-here

0

A

normal

 

 

0

A/F

normal

 

 

1

A

base-deleted

B

 

1

A/F

base-deleted

 

 

1

B

normal

 

1

1

B/F

normal

 

1

2

B/G

normal

 

 

 

 

table E4

 

 

Updating a Move

When a single-revision BASE tree is updated from one revision to another it temporarily becomes mixed revision (for both v1 and v2 editors). We cannot use the mixed-revision copy representation for these mixed-revision states as mixed-revision copies are represented as multiple-op-depth nested copies. The use of multiple-op-depth for mixed-revision moves would interfere with the use of multiple-op-depth for copies inside move destinations.

A typical move of A/B to X looks like:

op-depth

local-relpath

presence

revision

moved-to

moved-here

0

A

normal

6

 

 

0

A/B

normal

6

 

 

0

A/B/C

normal

6

 

 

2

A/B

base-deleted

 

X

 

2

A/B/C

base-deleted

 

 

 

1

X

normal

6

 

1

1

X/C

normal

6

 

1

 

 

table A1

 

 

 

If the working copy is updated the base tree changes, say from A@6 to A@8 where something inside the deleted tree changes:

op-depth

local-relpath

presence

revision

moved-to

moved-here

0

A

normal

8

 

 

0

A/B

normal

8

 

 

0

A/B/C

normal

8

 

 

0

A/B/D

normal

 

 

 

2

A/B

base-deleted

 

X

 

2

A/B/C

base-deleted

 

 

 

1

X

normal

6

 

1

1

X/C

normal

6

 

1

 

 

table A2

 

 

 

At present this produces a tree-conflict on A/B since the changes made to A/B are in the deleted tree. It also "breaks" the move since the move source and destination revisions no longer match. To "fix" the move the destination has to be modified to match the source. This involves changing the revsion of X and X/C, adding new nodes like X/D and removing deleted nodes (none in this example).

op-depth

local-relpath

presence

revision

moved-to

moved-here

0

A

normal

8

 

 

0

A/B

normal

8

 

 

0

A/B/C

normal

8

 

 

0

A/B/D

normal

 

 

 

2

A/B

base-deleted

 

X

 

2

A/B/C

base-deleted

 

 

 

1

X

normal

8

 

1

1

X/C

normal

8

 

1

1

X/D

normal

8

 

1

 

 

table A3

 

 

 

We also have to consider updating the working files. File that are unchanged between r6 and r8 need no update. If there are changes between r6 and r8 and the working file is unmodified then the working file can simply be replaced, but if the working file is modified then the r6 to r8 changes need to merged into the working file. Changes to the working file need to be made via the workqueue mechanism so that they happen if and only if the NODES row changes.

Resolving a Conflict Produces New Conflicts

When the move update needs to add or delete rows from the destination, rather than simply updating the revision, that may lead to new tree conflicts. Suppose I start with table A1 and add X/D:

op-depth

local-relpath

presence

revision

moved-to

moved-here

0

A

normal

6

 

 

0

A/B

normal

6

 

 

0

A/B/C

normal

6

 

 

2

A/B

base-deleted

 

X

 

2

A/B/C

base-deleted

 

 

 

1

X

normal

6

 

1

1

X/C

normal

6

 

1

2

X/D

normal

 

 

 

 

 

table C1

 

 

 

After the move destination is updated X/D will be 'R'eplaced:

op-depth

local-relpath

presence

revision

moved-to

moved-here

0

A

normal

8

 

 

0

A/B

normal

8

 

 

0

A/B/C

normal

8

 

 

0

A/B/D

normal

 

 

 

2

A/B

base-deleted

 

X

 

2

A/B/C

base-deleted

 

 

 

1

X

normal

8

 

1

1

X/C

normal

8

 

1

1

X/D

normal

8

 

1

2

X/D

normal

 

 

 

 

 

table C2

 

 

 

  • No labels