in source/com.microsoft.tfs.core/src/com/microsoft/tfs/core/clients/versioncontrol/soapextensions/Workspace.java [1084:1444]
public int checkIn(
PendingChange[] changes,
final String committer,
final String committerDisplayName,
String author,
String authorDisplayName,
final String comment,
final CheckinNote checkinNote,
WorkItemCheckinInfo[] associatedWorkItems,
final PolicyOverrideInfo policyOverrideInfo,
final CheckinFlags flags) throws CheckinException {
Check.isTrue(
changes == null || changes.length > 0,
"changes must be null for server-side change selection or non-empty"); //$NON-NLS-1$
Check.notNull(flags, "flags"); //$NON-NLS-1$
/**
* TFS 2010 behaves strangely with gated check-ins if we send null for
* associated work items (a non-standard subcode comes back in the
* ActionDeniedBySubscriberException. Always send at least an empty
* list.
*/
if (associatedWorkItems == null) {
associatedWorkItems = new WorkItemCheckinInfo[0];
}
final TaskMonitor monitor = TaskMonitorService.getTaskMonitor();
/*
* The total work for our progress monitor is set to 100, and subtasks
* are allocated as percentages. For example, checkin for conflicts is
* quick, so it takes only 2 percent. Uploading files usually takes
* longer, so it's 80 percent. Make sure all the work done in this
* method adds to 100.
*/
monitor.begin("", 100); //$NON-NLS-1$
/*
* We sort the changes by server path so they appear in the correct
* order when giving status information to the user.
*/
String[] serverItems = null;
if (changes != null) {
changes = changes.clone();
Arrays.sort(changes, new PendingChangeComparator(PendingChangeComparatorType.SERVER_ITEM));
serverItems = PendingChange.toServerItems(changes);
}
// Lets us detect all abnormal exits (Throwable, Exception, Gated
// checkin exception) for saved checkin reset
boolean success = false;
try {
TaskMonitorService.pushTaskMonitor(monitor.newSubTaskMonitor(75));
try {
// Upload contents
if (changes != null) {
final CheckinEngine ci = new CheckinEngine(client, this);
final long start = System.currentTimeMillis();
ci.uploadChanges(changes, false, getLocation() == WorkspaceLocation.LOCAL);
log.debug(MessageFormat.format(
"total time for upload of {0} was {1} ms", //$NON-NLS-1$
changes.length,
(System.currentTimeMillis() - start)));
} else {
log.debug("null changes (server side change selection), skipped upload"); //$NON-NLS-1$
}
} finally {
TaskMonitorService.popTaskMonitor(true);
}
if (author == null) {
author = VersionControlConstants.AUTHENTICATED_USER;
}
if (authorDisplayName == null) {
authorDisplayName = UserNameUtil.getCurrentUserName();
final String domainName = UserNameUtil.getCurrentUserDomain();
if (!StringUtil.isNullOrEmpty(domainName)) {
authorDisplayName = UserNameUtil.format(authorDisplayName, domainName);
}
}
/*
* Finally, create a Changeset and send it to the server to be
* committed. It's important to pass "null" for the date so TFS 2010
* and later do not require CheckinOther permissions (required to
* set a specific date on a new changeset).
*/
final Changeset changeset = new Changeset(
null,
comment,
checkinNote,
policyOverrideInfo,
committer,
committerDisplayName,
null,
-1,
author,
authorDisplayName,
null);
/*
* Test one final time before the change set is fully committed.
*/
if (monitor.isCanceled()) {
// Caught in this method below.
throw new CoreCancelException();
}
monitor.setCurrentWorkDescription(Messages.getString("Workspace.CheckinInNewChangeset")); //$NON-NLS-1$
final AtomicReference<Failure[]> failures = new AtomicReference<Failure[]>();
final AtomicReference<Failure[]> conflicts = new AtomicReference<Failure[]>();
final boolean noAutoResolve = flags.contains(CheckinFlags.NO_AUTO_RESOLVE);
final CheckinResult result;
try {
/*
* If changes was null when this method was called, serverItems
* will be null here, which causes the server to check in all
* workspace changes.
*/
result = getClient().getWebServiceLayer().checkIn(
getName(),
getOwnerName(),
serverItems,
changeset,
makeCheckinNotificationInfo(associatedWorkItems),
flags,
null,
conflicts,
failures,
false,
0,
client.mergeWithDefaultItemPropertyFilters(null));
} catch (final ActionDeniedBySubscriberException e) {
if (e.getSubscriberType().equals(BUILD_CHECKIN_SUBSCRIBER) && e.getStatusCode() == 1) {
/*
* For ease of use, convert the
* ActionDeniedBySubscriberException into a stronger type,
* GatedCheckinException. This exception has helper
* properties and is typed in a way that customers expect.
* It is still an ActionDeniedBySubscriberException.
*/
throw new GatedCheckinException(e);
} else {
/*
* Some other subscriber has denied the decision point.
* Throw the ActionDeniedBySubscriberException verbatim.
*/
throw e;
}
}
monitor.worked(10);
changeset.setChangesetID(result.getChangeset());
// Report any failures.
reportCheckinConflictsAndThrow(result, conflicts.get(), failures.get(), noAutoResolve);
/*
* When the SetFileTimeToCheckin workspace option is set, then the
* full checkin manifest is returned to the client in the form of
* GetOperations, even in a server workspace. (In a server
* workspace, the local version updates are still performed by the
* server at the end of the CheckIn call.) We use this manifest to
* set the check-in date on each item in the changeset, even
* implicitly included missing parents and affected items of
* recursive changes.
*/
final TaskMonitor setFileTimeMonitor = monitor.newSubTaskMonitor(5);
try {
if (getOptions().contains(WorkspaceOptions.SET_FILE_TO_CHECKIN)) {
final GetOperation[] updates = result.getLocalVersionUpdates();
if (updates != null && updates.length > 0) {
setFileTimeMonitor.begin(Messages.getString("Workspace.SettingFileTime"), updates.length); //$NON-NLS-1$
for (final GetOperation getOp : updates) {
if (ItemType.FILE == getOp.getItemType()
&& null != getOp.getTargetLocalItem()
&& new File(getOp.getTargetLocalItem()).exists()) {
setFileTimeMonitor.setCurrentWorkDescription(getOp.getTargetLocalItem());
try {
final FileSystemAttributes attributes =
FileSystemUtils.getInstance().getAttributes(getOp.getTargetLocalItem());
boolean restoreReadOnly = false;
/*
* Temporarily remove the read-only flag so
* we can modify the time (Windows requires
* this).
*/
if (attributes.isReadOnly()) {
attributes.setReadOnly(false);
FileSystemUtils.getInstance().setAttributes(
getOp.getTargetLocalItem(),
attributes);
restoreReadOnly = true;
}
new File(getOp.getTargetLocalItem()).setLastModified(
result.getCreationDate().getTimeInMillis());
if (restoreReadOnly) {
attributes.setReadOnly(true);
FileSystemUtils.getInstance().setAttributes(
getOp.getTargetLocalItem(),
attributes);
}
} catch (final Exception e) {
client.getEventEngine().fireNonFatalError(
new NonFatalErrorEvent(EventSource.newFromHere(), this, e));
}
}
}
}
}
} finally {
setFileTimeMonitor.done();
}
/*
* If this is a server workspace, set files read-only.
*/
final TaskMonitor makeReadOnlyMonitor = monitor.newSubTaskMonitor(5);
try {
if (changes != null && getLocation() == WorkspaceLocation.SERVER) {
makeReadOnlyMonitor.begin(Messages.getString("Workspace.SettingReadOnly"), changes.length); //$NON-NLS-1$
for (final PendingChange change : changes) {
if (change.getChangeType().contains(ChangeType.EDIT)
&& change.getLocalItem() != null
&& new File(change.getLocalItem()).exists()) {
makeReadOnlyMonitor.setCurrentWorkDescription(change.getLocalItem());
try {
final FileSystemAttributes attributes =
FileSystemUtils.getInstance().getAttributes(change.getLocalItem());
if (!attributes.isSymbolicLink() && !attributes.isDirectory()) {
attributes.setReadOnly(true);
FileSystemUtils.getInstance().setAttributes(change.getLocalItem(), attributes);
}
} catch (final Exception e) {
client.getEventEngine().fireNonFatalError(
new NonFatalErrorEvent(EventSource.newFromHere(), this, e));
}
} else {
// Skipping this one.
makeReadOnlyMonitor.setCurrentWorkDescription(""); //$NON-NLS-1$
}
makeReadOnlyMonitor.worked(1);
}
}
} finally {
makeReadOnlyMonitor.done();
}
monitor.setCurrentWorkDescription(Messages.getString("Workspace.NotifyingListeners")); //$NON-NLS-1$
/*
* Determine which pending changes were committed and which were
* undone. Preserve the sorted order in the sublists.
*/
PendingChange[] committedChangesArray = new PendingChange[0];
PendingChange[] undoneChangesArray = new PendingChange[0];
if (changes != null && changes.length > 0) {
final Set<String> undoneServerItems = new TreeSet<String>(ServerPath.TOP_DOWN_COMPARATOR);
for (final String undoneServerItem : result.getUndoneServerItems()) {
undoneServerItems.add(undoneServerItem);
}
final List<PendingChange> undonePendingChanges = new ArrayList<PendingChange>(undoneServerItems.size());
final List<PendingChange> committedPendingChanges = new ArrayList<PendingChange>();
for (final PendingChange change : changes) {
if (undoneServerItems.contains(change.getServerItem())) {
undonePendingChanges.add(change);
} else {
committedPendingChanges.add(change);
}
}
committedChangesArray =
committedPendingChanges.toArray(new PendingChange[committedPendingChanges.size()]);
undoneChangesArray = undonePendingChanges.toArray(new PendingChange[undonePendingChanges.size()]);
}
// Notify the user that the checkin iCheckinEvents complete.
client.getEventEngine().fireCheckin(
new CheckinEvent(
EventSource.newFromHere(),
this,
result.getChangeset(),
committedChangesArray,
undoneChangesArray));
Workstation.getCurrent(getClient().getConnection().getPersistenceStoreProvider()).notifyForWorkspace(
this,
Notification.VERSION_CONTROL_PENDING_CHANGES_CHANGED);
monitor.worked(1);
final int cset = changeset.getChangesetID();
TaskMonitorService.pushTaskMonitor(monitor.newSubTaskMonitor(4));
try {
/*
* Only update work items if we have a valid (non-0) changeset.
* Changeset 0 indicates all the pending changes were undone on
* the server.
*/
if (cset != 0) {
updateWorkItems(associatedWorkItems, cset, comment);
}
} finally {
TaskMonitorService.popTaskMonitor(true);
}
// Remove any saved attempted checkin info.
setLastSavedCheckin(buildEmptyLastSavedCheckin());
success = true;
return cset;
} catch (final CanceledException e) {
// Fire as non-fatal
client.getEventEngine().fireNonFatalError(
new NonFatalErrorEvent(
EventSource.newFromHere(),
this,
new CoreCancelException(Messages.getString("Workspace.CheckinCancelled")))); //$NON-NLS-1$
return 0;
} catch (final CoreCancelException e) {
// Convert to CanceledException and fire as non-fatal
client.getEventEngine().fireNonFatalError(
new NonFatalErrorEvent(
EventSource.newFromHere(),
this,
new CanceledException(Messages.getString("Workspace.CheckinCancelled")))); //$NON-NLS-1$
return 0;
} finally {
/*
* If the checkin didn't succeed, save the info for the next
* attempt. success will be false for expected things like gated
* checkin and cancelation exceptions, and also for unexpected
* exceptions.
*/
if (!success) {
updateLastSavedCheckin(comment, checkinNote, associatedWorkItems, policyOverrideInfo);
}
monitor.done();
}
}