Nowadays, almost all modern SCMs give users some way to handle directory versioning. But this was definitely not the case just some years ago, and is still a problem with systems so spread as SourceSafe, CVS or even Perforce (ok, the last one can handle versioning but... just try to rename a file and see how painful it can get).
So, what's different with Plastic?
Well, most of the SCMs providing directory versioning somehow store the file's path together with its data or metadata. What does it mean? Well, if you have a file named /src/foo.c, it means that somewhere they store file's name plus path associated to file's specific data (like revision number, owner, and so on). So when you actually rename a file (you can try with Subversion), a new revision for this file is created. The file really knows about its location. In fact some of these systems handle renaming as a delete plus add operation, the best ones taking care of keeping element's history on the process. The element is somehow tied to its location. This approach greatly simplifies internal implementation, but imposes some limitations in the way operations like renaming or moving are handled. Why? Well, here is where the complex part starts...If you are working on a single branch some of them won't have problems renaming or moving. But the funny thing comes during the branch and merge cycle. In Subversion, for instance, if you create a branch, rename a file there, and merge it back to the trunk, you will end up having two revisions of the file: a new file is created during the merge operation. This behaviour happens because the rename (or move, internally are normally handled in the same way) operation is an element one and not a directory operation. So conflicts are not handled the same way.
In Plastic, items (files or directories) don't store data about its location or name. This information (name) is stored by its containing directory. This approach provides a big advantage during merge operations: renames, moves, additions and so on are handled as directory conflicts. So in Plastic, if you rename something on a branch and you merge it back... you get it renamed!
But there is much more on the approach. In Plastic you always need a selector to be able to solve item's paths. Why? If you consider the other systems, you will see they are all able to exaclty identify item's paths just by looking at the trunk or at a given branch. This happens because all information is completely stored in the branch. Team System, Perforce or Subversion they all implement what is known as path space branching, meaning that branches and directories are quite close to each other at an implementation level. That's why when you branch on these systems, a full copy (ok, an inexpensive one, but a copy) of the whole content is created. They gain implementation's simplicity at the cost of loosing the ability to implement features like branch inheritance or really fast branching.
But, still, you are probably wondering... ok, but weren't we talking about directories? Why are you talking about branches then? In Plastic a branch just contain the revisions that where actually modified on that branch, so there is not full information about the paths of those revisions. So, imagine you modify /src/foo.c on br:/main/branch100, the revision created at the branch doesn't store information about the /src directory. How is then possible to know where it is?
It all depends on the selector. Imagine we have the following on main branch:
/
/src
/src/foo.c
Consider that we have two revisions of foo.c on main: /src/foo.c#0 /src/foo.c#1
Then, if we modify foo.c on /main/branch100, a selector like the following
rep "default"
path "/"
br "/main/branch100"
co "/main/branch100"
Will be able to correctly locate foo.c under /src, because it will go through the following process:
- Get a revision for /. There will be no revision on /main/branch100 but there is one on main, so Plastic will take it.
- Get all / children. In this case only /src will come. Now a revision must be obtained.
- Get a revision for /src according to the selector. Because there is no revision on /main/branch100, latest on /main will be selected.
- Get /src children. /main/foo.c is the only one.
- Get a revision for the found file. One is found at the /main/branch100 branch. Its last revision on the branch will be downloaded.
As you can see item's location will depend on the selector, and not on the branch. Normally all selectors will share a similar structure, but it can happen that the same revision could show up on different locations depending on the selector.
Suppose we have two branches named /br00 and /br01. We still have /src/foo.c on main, so we will first work with the following selector:
rep "default"
path "/"
br "/br00" co "/br00"
path "/"
br "/main" co "/br00"
Which means: take whatever is on /br00, but if there is nothing, take latest from /main, and place check outs always on /main.
Now suppose you make the following modifications:
cm co src
mkdir src/directory1
cm add src/directory1
cm mv src/foo.c src/directory1/bar.c
cm ci src src/directory1
Which means: create a subdirectory /src/directory1 and move foo.c into it renaming into bar.c. Notice that we haven't created any new revision for the item known as foo.c (and now known as bar.c).
Now, if we want to refer to foo.c, how should we do it? Because according to the latest selector it will be /src/directory1/bar.c, but a different selector could place it in another location. Ok, you may now think: yes, but knowing the branch we know the exact location. The answer is no. If you look carefuly into the sample you will see br00 just contains revisions of src and directory1, so there isn't a full path stored on the branch.
Suppose now the following selector:
rep "default"
path "/"
br "/br01" co "/br01"
path "/"
br "/main" co "/br01"
And the following operations:
cm co .
mkdir dirbase
cm add dirbase
cm mv src dirbase
cm ci . dirbase
Now, where is our old foo.c located? Well according to the last selector it will be at /dirbase/src/foo.c, but, what would happen with the next selector?
rep "default"
path "/"
br "/br00" co "/br01"
path "/"
br "/br01" co "/br01"
path "/"
br "/main" co "/br01"
Our file would show up under /dirbase/src/directory1/bar.c.
Let's now modify the file:
cm co /dirbase/src/directory1/bar.c
Finding the checkouts now will reveal (with the same selector) that file /dirbase/src/directory1/bar.c.
Let's check the file in, and switch selector to a regular one pointing to /main:
rep "default"
path "/"
br "/main" co "/main"
If we now merge from br01, we will get
/
/dirbase
/dirbase/src
/dirbase/src/foo.c
If we never merge /br00, the path /dirbase/src/directory1/bar.c will never exist on /main, or it even could be a path completely dependent on the selector rules.
It is important to highligth that Plastic's selectors are extremely important to understand how the system works, and the full range of possibilities that it provides. Item's location, opposite to what happens on other systems, don't depend on the internal's system structure but on the way the user want to look into the system.
0 comments:
Post a Comment