Team Foundation Version Control Concepts: Items versus Item Names

Version control is used to store information, generally files and folders. In Team Foundation Server version control, files and folders are known as items. Team Foundation Server rarely makes a distinction between files and folders because both behave so similarly; files and folders are both stored as rows in the same table. This article discusses how Team Foundation version control classifies items and item names so that people who use the product will have a better understanding of these concepts.

Note

This article is based on blog posts made by Richard Berg: T F S Version Control Concepts 1: Items and TFS Version Control Concepts 2: Item Names.

What are the Characteristics of Items?

Items have the following qualities:

  • Items are unique. Each item has an ID unique to that item.
  • Items are versioned. Like all version control systems, Team Foundation version control makes it easy to store successive versions of the same item and retrieve old versions.
  • Items can have more than one name. At a rudimentary level, items have a server path that shows where they live on the server and a local path that shows where they live on each user's local computer. Names are not unique.
  • Items have a type. Items can be an ANSI, UTF-16, Binary, or similar file, or they can be a directory.

Understanding Folders as Items

Any operation involving items can potentially be recursive. As a result, many operations, such as deleting a folder, are more complex than they first appear. When you delete a folder, the user interface makes this task appear as a single recursive operation. The server also considers this operation a single recursive operation so that the history for the item will show the same recursive format. If folders were not considered items, the delete operation could not be performed without a large performance cost. For more information, see How to: Delete Files and Folders from Version Control.

Non-unique names also apply to folders. This concept can be more difficult to understand for folders than with files. Typically, we are familiar with the idea that data.txt at one point in time might differ from data.txt in the future. These differences could manifest as a different version of the same file or as two separate files. The same scenario can also exist for folders.

In Team Foundation version control, a distinction must be made between adding, deleting and then renaming a folder to use the same name as the deleted folder, and adding, deleting, and then un-deleting a folder.

Figure 1

Cc707804.a2fa2722-8d5e-402f-8ee4-1ec0142565eb(en-us,VS.90).png

In the first case, you actually create two items, whereas in the second case, you create different versions of a single item. When you delete a folder, you also always delete the child items in the folder. Undelete behaves the same way; when you undelete the folder, you also undelete all child items in the folder.

Item Names

Names, or paths, are the user-friendly way in which to identify items. Rarely do you know the ItemID for the item you intend to modify before you modify it. Because names are not first-class objects in the version control system, we need a way to map names back to their corresponding items.

Namespaces

Each consistent class of item names is called a namespace. The rules for translating a name in a certain namespace to an item - or to another namespace - are called mappings. Mappings are also named functions or transformations. The easiest way to describe mappings is by using terms borrowed from math. Some mappings are injective, or one-to-one, and others are surjective, or many-to-one. Sometimes an explicit set of rules defines the mapping; sometimes the mapping is more of a side effect.

Server space

Server space, or committed space, is what you see when you work in Source Control Explorer. Server space represents the server paths of the items checked in to source control. The mapping between items and the server space is one of the fundamental properties tracked with each revision to an item, although there is no API to perform the transform explicitly. In addition, the mapping of an item to server space is injective. At any given point in time, each item has exactly one name in server space. Mappings become trickier over time as users commit renames.

Local workspace

Local workspace is where local file system paths live. Local workspace is defined in relation to server space by workspace mappings, which are explicit and easily exposed to the user. This mapping is surjective; every item in a local workspace maps back to an item in server space. The mapping is usually not injective, unless you map the whole repository ($/) and have no cloaks. For more information, see Working with Version Control Workspaces and Version Control Workspaces and Mapping Overview.

Note

On the server, there is also a mapping directly from items to each local workspace. This mapping is injective and helps increase performance by tracking local version information. For the purposes of this article, this mapping type is not discussed because it is not conceptually important.

Pending space

Pending space is also a property of a local workspace. Pending space represents the server paths of items as they would look in server space after you checked in all your currently pending changes. In other words, pending space is defined implicitly, by transforming server space according to any pending renames you have in that workspace. Like local workspace, this mapping is usually surjective only; items in pending space map back to server space, but not vice versa.

Target space

Target space is a reflection of server space from one branch onto another. The Merge operation must generate a mapping between items in the source branch, or server space, and items in the target branch, or target space, in order to determine the correct target items apply the pending changes on. The mapping is implicitly defined by the history of the two branches; any renames that have occurred, whether they were merged, and if so, how they were resolved. Typically, the mapping is neither injective nor surjective, because items can be moved in and out of the two branches at will.

Namespace Examples

The scenarios in this section illustrate the namespace concepts discussed in this article.

Suppose you performed the following operations, as listed in this history chart:

Changeset Action

10

Add $/project/branch1/test/data.cs

20

Branch it to $/project/branch2/test/data.cs

For the purposes of this example, the ItemIDs for each item will stay the same, and the names in server space are specified exactly as shown in the history charts.

Based on the information in the history chart, you could create a local workspace with these mappings:

$/project = c:\myProj
$/project/branch2 = c:\myProj2

After the workspace is synchronized to changeset 20, you can see that the two items have local names: c:\myProj\branch1\test\data.cs and c:\myProj2\test\data.cs. Edits are the simplest kind of pending change in this context. If you make a pending Edit on the items, their names in pending space would just be the same server paths as found in server space. The names are shown in the following table.

Server Local Pending

$/project/branch1/test/data.cs

c:\myProj\branch1\test\data.cs

$/project/branch1/test/data.cs

$/project/branch2/test/data.cs

c:\myProj2\test\data.cs

$/project/branch2/test/data.cs

Pending a namespace operation, such as an Add, Branch, Delete, Undelete, or Rename, provides more interesting results. For example, if you created a third branch, that branch would have a local path and a server path in pending space, c:\myProj\branch3 and $/project/branch3, but would not occur in server space. Add and Undelete work similarly; Delete is the reverse. For more information, see Tf Command-Line Utility Commands.

Rename is the most complex. For the purposes of this example, let's check in one Rename and make another pending Rename as shown in this history chart.

Changeset Action

30

Rename $/project/branch1/test

to

$/project/branch1/test-ren

40

Merge branch1 with branch2.

Resolve the rename conflict as AcceptYours, which keeps the target name unchanged. For more information, see Resolve Command and Resolving Folder Differences and File Conflicts.

[pending]

Rename $/project/branch2/test/data.cs

to

$/project/branch2/test/data-ren.cs

At this point, the original two items have the following names:

Server Local Pending

$/project/branch1/test-ren/data.cs

c:\myProj\branch1\test-ren\data.cs

None.

$/project/branch2/test/data.cs

c:\myProj2\test\data-ren.cs

$/project/branch2/test/data-ren.cs

One of the downstream effects of recursive operations is that the first item has changed names even though it has never been renamed. When you want to determine the name of a specific item at a specific time, you cannot just view the history of the item. You have to consider the history of its parent folder, grandparent folder, and so on, and also any parent folder it was ever part of. While the changeset that became #30 was still pending, a similar process had to be followed in order to compute its name in pending space.

Second, notice that the names of related items have diverged between the two branches. Target space occurs because that relationship must be preserved. For the purposes of this example, let's check in a second rename and make another pending merge, as shown in this history chart:

Changeset Action

50

Rename $/project/branch2/test/data.cs

to

$/project/branch2/test/data-ren.cs

[pending]

Merge branch2 with branch1. Resolve the rename conflict as an AcceptMerge.

Merge sees that there has been a rename to data.cs in the source, branch2, and must make the pending change in the target, branch1. By looking at the merge history, it determines which target item is related to branch2/test/data-ren.cs, that is the name of the item in target space. During the first merge, the process was straightforward; merge just substituted the branch root, $/project/branch2. This time, the process is more complex, as show in the following table:

Server Local Pending Target

$/project/branch1/test-ren/data.cs

c:\myProj\branch1\test-ren\data-ren.cs

$/project/branch1/test-ren/data-ren.cs

None.

$/project/branch2/test/data-ren.cs

c:\myProj2\test\data-ren.cs

None.

$/project/branch1/test-ren/data.cs

See Also

Other Resources

Team Foundation Version Control Naming Syntax, Conventions, and Limitations
Working with Version Control Files and Folders
Working with Version Control Changesets
Pending Changes