Branches
Cloud CMS stands apart from just about every other content management product on the market in that it offers
a versioning model that is based around changesets. It is inspired by Git and Mercurial which are popular
source-control products within the software development world. As such, Cloud CMS provides a changeset
versioning system that captures every touch - every create, update or delete - that you make.
Nothing is ever lost. Every data operation is fully transactional with every transaction payload is fully
captured into its own changesets. Transactions span multiple documents and full "units of work" are written into
the changeset history with every operation.
In the end, the changeset history is fully descriptive of every operation and data change that has occurred. This
allows you to snapshot the repository at any moment in time. Or to roll back or forward to a specific moment in time.
This "Copy on Write" approach ensures that you're always able to make changes and restore a previous state should
something go wrong. It allows you to try things out, see how they look and then approve them for go live or roll back
in case you don't like it turned out. It is also the mechanism by which you can create vast updates, stage them for
release and then push them live at a certain point in time.
Branches
Similar to Git or Mercurial, Cloud CMS offers branches so that you may maintain multiple, concurrent workspaces
within which content is being worked on. Every repository has a master branch. A new branch is created by
forking the master branch.
You can create as many branches as you like (by forking a new branch off from a previous branch). For all practical
purposes, you can think of a branch as a copy of the branch from which it is forked. If you fork the master branch,
then your new branch is basically a copy of the master branch. Under the hood, the implementation is optimized so that
there isn't a literal copy of stuff until you touch something. But in essence, it is the same idea.
Once you have a branch, you can work on it at your own pace. The branch is effectively a copy and so if you make
changes to things on it, you won't affect others. A change to a piece of content on a branch doesn't affect the
original content on the original branch.
So you're free to make changes and try things out. If you don't like the way things are going, you can simply
discard the branch. This tears down the branch and throws it away. Or, if you have something on the branch that
you think might be interesting some day, you can keep it around. For example, you could freeze the branch to
prevent others from making changes and then simply switch to another branch to keep on working.
Branches effectively provide expendable workspaces where you can build things at your own pace. Once you have
something that you're happy with, you can merge the content. You can either merge the content back to the branch
from which you forked, which is the most common scenario, you can merge the content to a completely different branch.
This is sometimes referred to as pushing and pulling content between branches.
Tip View
Every branch that you create automatically maintains a "tip view" of the content in that branch within
Mongo DB and Elastic Search. The "tip view" contains all of the content as seen from the tip (where all deletes are
actually removed, updates write over creates and so on).
At the same time, branches also fully capture every create, update and delete is retained in a master record. This
master record can then be used at any time to reconstruct the state of the branch at any moment in time.
Using these "tip views", Cloud CMS provides support for Elastic Search and MongoDB on a per-branch basis. Each branch
maintains its own managed indexes to support these. Thus, it is possible and very common within Cloud CMS to have
two branches that yield completely different search or query results. In fact, this should very much be the case
if you're using branches in any kind of interesting way.
Thus, Cloud CMS makes sure that branches are moving targets. You can make changes to them and Cloud CMS maintains
all of the indexes and things behind the scenes so that they just work as expected. You just flip branch IDs in
your API calls and you can run the same application logic against different sets of data.
Snapshots
A snapshot is a special kind of branch that is always frozen. You can create them at any time by identifying a
changeset against which they should be created. A snapshot computes all of these indexes and everything else but
locks itself against that specific changeset. It is used to capture a moment in time.
Snapshots are useful in two ways:
- to provide your application with a "released" or "approved" view of your content. By creating a snapshot, you
produce a frozen branch that can be used within your API calls to inspect the repository at that exact moment in time. - to improve performance of changeset history calculation for differencing during merge. Snapshots basically provide
interleave frames that the algorithm can look to for a precomputed view of the world.
Aliases
Branches supports alias. Using an alias
, you can provide an alternate way to reference your branch from your
application. For example, you might have a live
alias that you switch from one branch to another branch when
you decide to go live with a new set of content.
Branch API
As noted, every repository has a master branch which is referred to as master
. This is the branch that is used
most often.
If you aren't taking advantage of branching within Cloud CMS, then chances are that you've been working against themaster
branch all this time without knowing it.
Read a Branch
From an API perspective, you can retrieve a repository branch like this:
GET /repositories/{repositoryId}/branches/{branchId}
Here is what the master branch might look like:
{
"_doc": "b7ccbaccf8dd30db947b",
"root": "0:root",
"tip": "24:897ec2b2d22bf7ee5ad4",
"title": "master",
"type": "MASTER",
"snapshot": false
}
Where the properties are these:
- root - the changeset where the branch begins and is rooted against
- tip - the latest changeset written to the branch
- title - a name for the branch
- type - either
MASTER
for the master branch orCUSTOM
for any other branch - snapshot - whether the branch is a snapshot
List Branches
You can list of all of the branches for a repository like this:
GET /repositories/{repositoryId}/branches
Create a Branch (Fork)
To create a branch, you must fork at an existing changeset.
POST /repositories/{repositoryId}/branches?changeset={changesetId}
Archive a Branch
When you're done using a branch, you may opt to archive it. Archiving a branch will free up disk resources from
both MongoDB and Elastic Search as it will tear down any runtime "tip views" that are being managed. That said, it
won't cause any data loss -- the master record is never affected. You can restore a branch's "tip view" at any time.
POST /repositories/{repositoryId}/branches/{branchId}/archive
Note that you cannot perform data operations against an archived branch. Any API calls that seek to create, read,
update, query or search content within an archived branch will not work. To do so, you will first need to
unarchive the branch.
Unarchive a Branch
When you want to restore a branch's runtime "tip view", you can unarchive it. You must unarchive a branch before
performing any operations that will create, read, update, query or search against the contents in a branch.
POST /repositories/{repositoryId}/branches/{branchId}/unarchive
Merge a Branch
You can start a merge of the contents of one branch into another like this:
POST /repositories/{repositoryId}/branches/{branchId}/merge/start?id={otherBranchId}
The merge process starts with the tips of both branches and then works backwards to find the changeset that they have
in common. In Cloud CMS, since all branches (except the master
branch) are required to have parent branches, there
is always a changeset somewhere in the past that is a common ancestor.
It then computes the changeset history for both branches starting from this common ancestral changeset and walking
forward to the tip changeset. These changeset histories are then compared and merged.
The merge is run as an asynchronous background job. This is because the merge may be very complex (for very long
changeset histories) and may take an indeterminant amount of time. As such, when you start a merge, you will get back
a JSON response like this:
{
"_doc": "JOB ID"
}
Which provides the ID of the running job. You can then poll this job to check when it completes and when it does,
you can inspect the job itself to see how the merge went.