Xlinks
Often, you have separate repositories for different projects. To share existing components from a different project, you can use Xlinks to mount the component in your project.
An Xlink is a directory entry in your repository that points to another directory in a different repository. The Xlink contains information on the specific version of the directory that it points to. You define an Xlink with these three following arguments:
- The directory entry to create in your current repository. This is a directory that you use.
- The target repository.
- The directory to mount in the target repository.
- The version of the directory to mount. This is either a changeset specification or a label specification.
For information on how to create an Xlink, refer to Create an Xlink.
Types of Xlink
Read only Xlinks (Xlink
)
A read only Xlink is ideal if you use an existing component but don’t influence the development of the component. This means you can use the linked component in your project, but can’t change it.
Writable Xlinks (wXlink
)
With a writable Xlink, you can make changes inside the Xlinked repository.
Partial Xlinks
Partial Xlinks are Xlinks that point to a specific relative path in another repository instead of the default root of the repository. Partial Xlinks are read only because writable partial Xlinks overcomplicate merges. You can create partial Xlinks only in the command line.
For example, the following command creates a read only partial Xlink that mounts the path /includes/opengl
in the Xlinked repository and ignores the rest of the repository:
cm xlink opengl\include /includes/opengl 1627@includes@localhost:8087
For more information on how to create Xlinks in the command line, use the command cm xlink –help
.
Warning: Don’t use partial read only Xlinks with Gluon as you’ll get an inconsistent workspace.
Complex Xlink structures
Since Xlinks can point to repositories that already contain Xlinks, component mounting can address complex development scenarios.
Versioning
You can specify, for example, that version 1.1 of project X uses version 2.1 of library Y.
When the new version 1.2 of project X is labeled, you can update the code of the project to use a new version 2.2 of library Y.
Since Xlinks are versioned, the Xlink in the original project still points to the original version of the library, so you can rebuild that configuration without issues.
Xlink expansion rules
Xlink expansion rules define how branches are created on your Xlinked repositories, and how the branches in the parent repository relate to the branches in the Xlinked repository.
UVCS automatically creates branches in linked repositories when you make changes.
Xlinks in top level branches
In the following example, you have two repositories, quake
and zlib
, and a writable Xlink (wXlink) between the main branch on quake
to the main branch on zlib
:
You then create a new branch, task001
in the quake
repository and modify an Xlinked file. Since you can make changes to a wXlink, UVCS automatically creates a matching task001
branch in the zlib
repository. To track the new branch connection, UVCS creates an expansion rule:
Source branch | Destination branch | Is defined by user |
---|---|---|
/main@quake@localhost:8087 | /main@zlib@localhost:8087 | Yes |
/main/task001@quake@localhost:8807 | /main/task001@zlib@localhost:8087 | No |
UVCS automatically creates new rules if more branches are automatically expanded through the branch hierarchy.
Xlinks in a second level branch
In the following example, you create a wXlink in a task branch, main/xlink-creation
, instead of your main branch:
Because you likely want to merge the main/xlink-creation
in to your main branch, you might not want to create an Xlink expansion rule from main/xlink-creation@quake -> main@zlib
. Instead, you likely want to Xlink the main branches.
UVCS detects this situation and creates a rule that connects the main branches: main@quake -> main@zlib
. To notify you, UVCS provides an information message to tell you that UVCS creates a new expansion rule to link the two main branches and that if you don't want that layout, you can edit the expansion rules manually.
Because UVCS automatically defines the wXlink between the main branches, if you then modify a file in the wXlink on main/xlink-creation
, the branch expansion works correctly and creates a matching main/xlink-creation
branch in the linked zlib
repository:
Source branch | Destination branch | Defined by user? |
---|---|---|
/main@quake@localhost:8087 | /main@zlib@localhost:8087 | Yes |
/main/task001@quake@localhost:8807 | /main/xlink-creation@zlib@localhost:8087 | No |
Note: You can manually edit the expansion rules if you want a different layout to the above example.
Asymmetric Xlink hierarchies
Symmetric links follow the same branch names, while asymmetric links connect different branch names.
In the previous two examples, the wXlinks are symmetric: main
linked to main
and main/task001
linked to main/task001
.
Expansion rules can be especially useful if you need asymmetric links. For example, if you need to link the branch main@quake
to branch main/fix@zlib
.
UVCS allows you to link repositories asymmetrically, and sets up expansion rules accordingly. These expansion rules let you manage more complex branching structures where the parent repository and the linked repository don’t follow the same branch naming conventions.
Example Xlink configuration
In the following xlink configuration example, a video game company has an engine that two different game studios use in different locations. You can use Xlinks to optimize this workflow and share the engine code with both studios and still continue individual development:
Each game studio’s repository has both an Xlinked engine folder that syncs with the remote main repository and a folder for their local game repository. Each game repository has an Xlink that points to the main Engine repository.
The main engine repository has a separate branch for each game. The separate branches allow you to handle any specifications related to each game, and allows each game studio to use and independently modify the engine code.
Note: Another advantage of separate branches is that you can cherry pick and merge any helpful changes or fixes into the main branch of the engine repository. You can propagate changes from one game to the other, and you don’t need to replicate the entire engine repository for each studio location, Just sync the desired branches and reduce disk and network usage. For example, studio A doesn’t need a copy of the studio B branch.
Example Xlink configuration
The Xlink for each game repository points to the game specific branch of the engine repository. Since both games are connected to the same engine repository, you need to set the branch auto expansion rules.
- Xlink branch expansion rules on the
GameA
repository:- source branch:
/main
- destination branch:
/main/GameA
- source branch:
- Xlink branch expansion rules on the
GameB
repository:- source branch:
/main
- destination branch:
/main/GameB
- source branch:
You also need to select the following options:
- Writable: you need to be able to make changes in the engine repository.
- Relative server: you work on a replicated local version of the repository and don’t need access to the remote server.
Merge Xlinks
When you merge a branch, the merge also affects the auto expanded branches in the wXlinked repositories. You do the merge operation on the top level repository (where the workspace points to) and UVCS also considers the changes in those linked repositories.
You only need to merge the top level repository; UVCS automatically merges any changes in linked repositories and updates the wXlink. As in any merge, you might need to manually resolve some merge conflicts in the Xlinked files.
This process keeps your Xlinked repositories in sync and ensures that you don't need to manually merge changes in the linked repositories separately.
Example merge with Xlinks
In the following example, you have a main repository called ProjectX
and another repository called MyLibrary
. ProjectX
has a writable Xlink (wXlink) that points to MyLibrary
.
If you have two branches, task002
and task003
, and make changes to an Xlinked file, events.h
, in both branches, the following happens when you merge:
- Merges the top level repository: UVCS first merges the changes inside the
ProjectX
repository. - Checks the linked repositories: UVCS looks for the wXlinks in
ProjectX
. Because you made changes toevents.h
in both branches, it sees thattask002
andtask003
point to different versions ofMyLibrary
. - Handles changes in linked repositories: The merge process merges the changes in
MyLibrary
to combine the changes toevents.h
from both branches. - Updates the wXlink: UVCS ensures that the wXlink updates to point to the newly merged version of
events.h
inMyLibrary
.
Relative Xlinks
When you replicate an Xlink, the Xlink still links to the original target repository. For example, if your Xlink originally pointed to a repository on mainserver.location1.com:8087
, when you copy it to another server, otherserver.location2.com:8087
, it still tries to access the original repository on mainserver
.
If you want everything to be local on your new server instead of depending on the original server, you can use relative Xlinks. A relative Xlink points to the local copy of the target repository in the same server as your main repository.
Note: A relative Xlink uses the GUID to identify changesets, so the relative Xlink points to the correct changeset even if they are numbered differently.
To create a relative Xlink, add the -rs
modifier. For example, cm xlink -rs component1 / 6@mylibrary
.