How to check in changes on behalf of other users
Recently someone asked how to check in changes for another user. There are at least two good scenarios where this is needed. One is for check-in systems such as the Gauntlet system we used internally during the development of TFS. Rather than check in directly, developers would shelve their changes and put them in the Gauntlet queue. Gauntlet would unshelve one shelveset’s worth of changes, build the code with those changes, run a small set of tests, and then check in the changes on behalf of the user who created the shelveset.
Another important scenario is for converting an existing system over to Team Foundation Server. For version control, you would like the history in TFS to reflect who actually made the changes, rather than the account used for the conversion.
We provide the capability to support these scenarios through a permission and parameters to the CheckIn() method, which supports the /author option on the checkin command in tf.exe. In order to be able to check in changes on behalf of another user, the user performing the check in must either be an administrator or must have the CheckinOther permission.
The MSDN documentation describes the CheckinOther permission and how to set it from within VS as well as from the command line. From the command line, you would execute something like the following. This example sets the permission for $/Project and all directories and files that inherit permission settings from $/Project.
tf perm $/Project /allow:CheckinOther /user:domainSomeOne
From the command line, you can check in changes for someone else by using the /author option to the the checkin command (documentation). For example, the following command checks in all changes in your current workspace on behalf of OtherUser (/author:domainOtherUser) without prompting (/i). You will need to specify domain if it is different than the domain of your user name (there’s no harm in always specifying it).
tf checkin /i /author:domainOtherUser
One important thing to note here is that the changes are pending in your workspace and are being checked in from your workspace. You are simply specifying what user name you want to see in the brief history format. Both your user name and OtherUser are recorded in the changeset data, so that you can always determine who checked in the changes.
All of the above discussion also applies to the CheckIn() method on the Workspace class in the version control API. I have copied the method signature and its documentation below. The overload below is the one that takes all of the parameters. The simplest CheckIn() overload requires only two parameters, an array of PendingChange objects and a comment, and it is used in the basic version control API example.
/// Check in a set of pending changes. Checkins are atomic – either they all succeed or
/// they all fail. If there are any conflicts that need to be resolved before the check in, the check
/// in operation will throw an exception.
/// If any part of the operations leading up to the final check in commit fail (uploading
/// files, etc) then the operation is aborted and an exception is thrown. If updating client
/// or work item state fails after the commit operation, a non-fatal error is reported with the
/// actual checkin having been successfully committed.
/// <param name=”changes”>List of changes to check in.</param>
/// <param name=”author”>if non-null, this identity will be listed in history for the committed
/// changeset (note that the user running this operation must have the CheckinOther
/// <param name=”comment”>Comment to go along with the checkin.</param>
/// <param name=”checkinNote”>Any applicable checkin notes data.</param>
/// <param name=”workItemChanges”>List of work item changes to occur with this checkin</param>
/// <param name=”policyOverride”>if non-null, policy failures are being overridden</param>
/// <param name=”checkinOptions”>Per-checkin options that affect event generation and
/// checkin author validation</param>
/// <returns>changeset number created by checking in</returns>
public int CheckIn(PendingChange changes, String author, String comment, CheckinNote checkinNote,
WorkItemCheckinInfo workItemChanges, PolicyOverrideInfo policyOverride,
public enum CheckinOptions
None = 1,
ValidateCheckinOwner = 2,
SuppressEvent = 4,
The author, checkinNote, workItemChanges, and policyOverride parameters can all be null if you don’t have values to specify (okay, the comment can be null too, but you wouldn’t do that, right?).
The CheckinOptions values allow you to control what happens with the author parameter (ValidateCheckinOwner) and server-side event generation (SuppressEvent). For overloads of CheckIn() that don’t specify the checkinOptions parameter explicitly, the value CheckinOptions.ValidateCheckinOwner is used.
For a Gauntlet-type scenario, calling the Checkin() method with the author being the owner of the shelveset is exactly what you need. You can call an overload that doesn’t explicitly specify the checkin options and use the default value.
The real purpose of the checkinOptions parameter is to support version control converters. For that scenario, you may need to check in changes on behalf of users that don’t exist as Windows users, either because they have been deleted or because the old version control system had its own user security system (e.g., Visual SourceSafe). So, most converters will not want to have the server validate the checkin owner (author).
Also for the conversion scenario, you will likely want to suppress checkin events on the server. The reason is that you are likely creating hundreds or thousands of changesets, and checkin event generation will simply add overhead to a process that you’d like to have run as quickly as possible. To do this, you will want to specify CheckinOptions.SuppressEvent for the checkinOptions parameter.
Putting this all together, the call to CheckIn() in a version control converter might look like the following. You may also want to specify the old system’s checkin date and other metadata in the checkin comment.
PendingChange pendingChanges = workspace.GetPendingChanges();
String oldSystemUser = /* get user name from other VC system */;
workspace.CheckIn(pendingChanges, oldSystemUser, “converted changeset”, null, null, null, CheckinOptions.SuppressEvent);
For those of you wondering why CheckinOptions.None and other None enum values have the value of 1 rather than zero, it was a side effect of wsdl.exe. In SOAP, the enumeration values are marshalled as names, such as None, rather than integers. As a result, the values of the enumerations in the client may not match the values of the enumerations used by the server, since they are specified in the web service’s WSDL.