in source/com.microsoft.tfs.client.clc/src/com/microsoft/tfs/client/clc/vc/commands/CommandResolve.java [76:390]
public void run() throws ArgumentException, MalformedURLException, CLCException, LicenseException {
final WorkspaceInfo cachedWorkspace = determineCachedWorkspace();
final TFSTeamProjectCollection connection = createConnection();
final VersionControlClient client = connection.getVersionControlClient();
initializeClient(client);
client.getEventEngine().addConflictResolvedListener(this);
parseResolutionOption();
final boolean preview = (findOptionType(OptionPreview.class) != null) || resolution == Resolution.NONE;
if (resolution != Resolution.ACCEPT_MERGE && findOptionType(OptionForce.class) != null) {
throw new InvalidOptionException(
Messages.getString("CommandResolve.ForceOnlyValidWithAutoMergeOrExternal")); //$NON-NLS-1$
}
Option o = null;
final ResolutionOptions options = new ResolutionOptions();
/*
* Detect if they want to override the encodings with a specific type
* (which is incompatible with conversion).
*/
if ((o = findOptionType(OptionOverrideType.class)) != null) {
reportBadOptionCombinationIfPresent(OptionOverrideType.class, OptionConvertToType.class);
options.setEncodingStrategy(
ResolutionOptions.EncodingStrategy.OVERRIDE_EXPLICIT,
((OptionOverrideType) o).getValueAsEncoding());
options.setAcceptMergeEncoding(((OptionOverrideType) o).getValueAsEncoding());
}
/*
* Detect if they want to convert to another encoding.
*/
if ((o = findOptionType(OptionConvertToType.class)) != null) {
options.setEncodingStrategy(
ResolutionOptions.EncodingStrategy.CONVERT_EXPLICIT,
((OptionConvertToType) o).getValueAsEncoding());
options.setAcceptMergeEncoding(((OptionConvertToType) o).getValueAsEncoding());
}
options.setUseInternalEngine(useExternalTool == false);
options.setAcceptMergeWithConflicts(findOptionType(OptionForce.class) != null);
ExternalToolset mergeToolset = null;
if (useExternalTool) {
/*
* Build a toolset with just one tool: the one the environment
* variable describes.
*/
mergeToolset = new ExternalToolset();
mergeToolset.addAssociation(new ExternalToolAssociation(new String[] {
ExternalToolset.WILDCARD_EXTENSION
}, CLCTools.getMergeTool()));
}
/*
* Calculate the path (if the user wants to override).
*/
String newName = null;
if ((o = findOptionType(OptionNewName.class)) != null) {
newName = ((OptionNewName) o).getValue();
}
/*
* Validate the new name against the resolution type and wildcards.
*/
if (newName != null) {
if (resolution != Resolution.ACCEPT_YOURS_RENAME_THEIRS && resolution != Resolution.ACCEPT_MERGE) {
throw new InvalidOptionException(
//@formatter:off
Messages.getString("CommandResolve.NewnameOnlyValidWithAutoMergeOrExternalOrKeepYoursRenameTheirs")); //$NON-NLS-1$
//@formatter:on
}
if (getFreeArguments().length > 1 || LocalPath.isWildcard(newName)) {
throw new InvalidFreeArgumentException(Messages.getString("CommandResolve.OnlyOneConflictWithNewname")); //$NON-NLS-1$
}
options.setNewPath(ItemPath.canonicalize(newName));
} else if (resolution == Resolution.ACCEPT_YOURS_RENAME_THEIRS) {
throw new InvalidFreeArgumentException(
Messages.getString("CommandResolve.NewNameRequiredWithKeepYoursRenameTheirs")); //$NON-NLS-1$
}
/*
* Query conflicts for the paths in the free arguments. If no free
* arguments available, send null to the web service to query the whole
* workspace.
*/
String[] resolvePaths = null;
if (getFreeArguments().length > 0) {
resolvePaths = new String[getFreeArguments().length];
for (int i = 0; i < resolvePaths.length; i++) {
resolvePaths[i] = ItemPath.canonicalize(getFreeArguments()[i]);
}
}
final Workspace workspace = realizeCachedWorkspace(cachedWorkspace, client);
Conflict[] conflicts = workspace.queryConflicts(resolvePaths, findOptionType(OptionRecursive.class) != null);
if (conflicts.length == 0) {
getDisplay().printLine(Messages.getString("CommandResolve.ThereAreNoConflictsToResolve")); //$NON-NLS-1$
return;
}
if (newName != null && conflicts.length != 1) {
throw new InvalidOptionException(Messages.getString("CommandResolve.OnlyOneConflictWhenNewname")); //$NON-NLS-1$
}
if (preview) {
for (int i = 0; i < conflicts.length; i++) {
ConflictPrinter.printConflict(conflicts[i], getDisplay(), false);
}
setExitCode(ExitCode.PARTIAL_SUCCESS);
return;
}
/*
* Check symbolic links
*/
boolean containsSymlink = false;
for (final Conflict conflict : conflicts) {
if (!StringUtil.isNullOrEmpty(conflict.getTargetLocalItem())
&& FileSystemUtils.getInstance().getAttributes(conflict.getTargetLocalItem()).isSymbolicLink()) {
containsSymlink = true;
break;
}
}
if (containsSymlink
&& !(resolution.equals(Resolution.ACCEPT_THEIRS)
|| resolution.equals(Resolution.ACCEPT_YOURS)
|| resolution.equals(Resolution.OVERWRITE_LOCAL))) {
getDisplay().printLine(Messages.getString("CommandResolve.SymlinksOnlyAcceptYoursOrTheirs")); //$NON-NLS-1$
return;
}
/*
* Resolve the conflicts.
*/
/*
* Collect the reported and resolved conflicts.
*/
final Set<Integer> reportedConflictIDs = new HashSet<Integer>();
final Set<Integer> resolvedConflictIDs = new HashSet<Integer>();
/*
* Contains server paths to directories that are conflicts, that were
* already resolved in the resolution loop, so conflicts underneath
* these directories can be skipped on that iteration.
*/
final List<String> resolvedFolders = new ArrayList<String>();
// Number of conflicts resolved per iteration in the loop.
int numResolvedConflicts = 0;
/*
* We may have to do multiple passes (each outside loop iteration is a
* pass) if the conflicts are not all resolved in the first pass.
*/
boolean tryAgain = false;
do {
numResolvedConflicts = 0;
resolvedFolders.clear();
Conflict[] conflictsResolvedThisIteration = new Conflict[0];
/*
* Each pass, iterate over the conflicts starting with the specified
* resolution and options.
*/
for (final Conflict conflict : conflicts) {
conflict.setResolution(resolution);
conflict.setResolutionOptions(options);
// There's no point in trying local conflicts without overwrite
// being specified.
if (resolution == Resolution.OVERWRITE_LOCAL && conflict.getType() != ConflictType.LOCAL) {
continue;
}
// If the conflict has already been processed then skip it.
if (resolvedConflictIDs.contains(conflict.getConflictID())) {
continue;
}
/**
* If this conflict is a child of a folder such that the folder
* was both resolved in this pass (sorted top down) and the
* folder had a rename or undelete and the resolution is accept
* merge and we're likely to do a three-way merge on the child,
* we want to wait until we get the updated conflict with the
* updated disk location before resolving the conflict.
*/
if (conflict.getResolution() == Resolution.ACCEPT_MERGE
&& conflict.getYourItemType() == ItemType.FILE
&& conflict.getYourServerItemSource() != null
&& conflict.canMergeContent()
&& conflict.getYourEncoding() != FileEncoding.BINARY
&& resolvedFolders.size() > 0) {
boolean affected = false;
for (final String directory : resolvedFolders) {
if (ServerPath.isChild(directory, conflict.getYourServerItemSource())) {
affected = true;
break;
}
}
if (affected) {
continue;
}
}
final ProcessFinishedHandler finishedHandler = new MergeToolFinishedHandler(getDisplay());
/*
* Resolve!
*/
try {
final AtomicReference<Conflict[]> conflictsHolder = new AtomicReference<Conflict[]>();
workspace.resolveConflict(
conflict,
conflictsHolder,
null,
mergeToolset,
finishedHandler,
System.out,
System.err);
conflictsResolvedThisIteration = conflictsHolder.get();
} catch (final ExternalToolException e) {
cleanupAfterFailedMerge(conflict);
/*
* Rethrow this, because it's fatal.
*/
throw e;
} catch (final VersionControlException e) {
cleanupAfterFailedMerge(conflict);
getDisplay().printErrorLine(e.getMessage());
}
/*
* Add to the lists so we can continue on other conflicts.
*/
if (conflict.isResolved()) {
/**
* Keep track of whether we have successfully resolved
* folders with accept merge where it was either rename or
* undelete. We don't want to work on the files under of
* those folders, because resolving the parent will change
* the on-disk location of the files.
*/
if (conflict.getYourItemType() == ItemType.FOLDER
&& conflict.getYourServerItemSource() != null
&& conflict.getResolution() == Resolution.ACCEPT_MERGE
&& (conflict.getBaseChangeType().contains(ChangeType.UNDELETE)
|| conflict.getBaseChangeType().contains(ChangeType.RENAME))) {
resolvedFolders.add(conflict.getYourServerItemSource());
}
numResolvedConflicts++;
for (final Conflict c : conflictsResolvedThisIteration) {
resolvedConflictIDs.add(c.getConflictID());
}
} else {
if (numResolvedConflicts == 0 && reportedConflictIDs.contains(conflict.getConflictID()) == false) {
/*
* If we couldn't resolve the conflict then say why.
*/
displayConflictResolveError(conflict);
reportedConflictIDs.add(conflict.getConflictID());
}
}
}
tryAgain = numResolvedConflicts != 0 && numResolvedConflicts != conflicts.length;
if (tryAgain) {
conflicts = workspace.queryConflicts(resolvePaths, findOptionType(OptionRecursive.class) != null);
}
} while (tryAgain && conflicts.length != 0);
final boolean haveUnresolvedLeft = conflicts.length != 0 && conflicts.length != numResolvedConflicts;
/*
* Print any conflicts that were not resolved.
*/
if (haveUnresolvedLeft) {
for (int i = 0; i < conflicts.length; i++) {
final Conflict conflict = conflicts[i];
if (conflict.isResolved() == false && reportedConflictIDs.contains(conflict.getConflictID()) == false) {
displayConflictResolveError(conflict);
}
}
}
if (haveUnresolvedLeft) {
setExitCode(ExitCode.PARTIAL_SUCCESS);
}
}