*** empty log message ***
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProvider.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProvider.java
index 1f94a5b..6a49a3f 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProvider.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProvider.java
@@ -12,6 +12,7 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.team.core.*;
+import org.eclipse.team.core.internal.Policy;
 
 /**
  * A concrete subclass of <code>RepositoryProvider</code> is created for each
@@ -61,9 +62,28 @@
 	 */
 	final public void configure() throws CoreException {
 		RepositoryProvider provider = RepositoryProviderType.getProvider(getProject());
-		if(provider!=null) {
-			throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "A provider is already associated with this project: " + provider, null));
-		}		
+		// Core Bug 11395
+		// When configure is called the nature has already been assigned to the project. This check will always
+		// fail. Also, if configure fails the nature is still added to the project.
+		//if(provider!=null) {
+		//	throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "A provider is already associated with this project: " + provider, null));
+		//}
+		// Alternate slower check
+		RepositoryProviderType[] types = RepositoryProviderType.getAllProviderTypes();
+		int count = 0;
+		for (int i = 0; i < types.length; i++) {
+			if(getProject().getNature(types[i].getID())!=null) {
+				count++;
+			}
+		}
+		if(count>1) {
+			try {
+				TeamPlugin.removeNatureFromProject(getProject(), getProviderType().getID(), null);
+			} catch(TeamException e) {
+				throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("RepositoryProvider_Error_removing_nature_from_project___1") + provider, null)); //$NON-NLS-1$
+			}
+			throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("RepositoryProvider_Too_many_providers_associated_with_project___2") + provider, null)); //$NON-NLS-1$
+		}			
 		configureProject();
 	}
 
@@ -118,6 +138,6 @@
 	 * @return a string description of this provider
 	 */
 	public String toString() {
-		return getProject().getName() + ":" + getProviderType();
+		return getProject().getName() + ":" + getProviderType(); //$NON-NLS-1$
 	}
 }
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderType.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderType.java
index ae18072..1c865b3 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderType.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderType.java
@@ -15,6 +15,7 @@
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.internal.Policy;
 
 /**
  * Describes a type of repository provider snf provides
@@ -42,7 +43,7 @@
 	 */
 	final public static void addProviderType(RepositoryProviderType providerType) throws TeamException {
 		if(providerTypes.containsKey(providerType.getID())) {
-			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "duplicate provider found in plugin.xml: " + providerType.getID(), null));
+			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("RepositoryProviderTypeduplicate_provider_found_in_plugin.xml___1") + providerType.getID(), null)); //$NON-NLS-1$
 		} else {
 			providerTypes.put(providerType.getID(), providerType);			
 		}
@@ -93,15 +94,16 @@
 	final public RepositoryProvider getInstance(IProject project) {
 		String id = getID();
 		try {
-			return (RepositoryProvider)project.getNature(id);
+			if(project.isOpen()) {
+				return (RepositoryProvider)project.getNature(id);
+			}
 		} catch(ClassCastException e) {
-			TeamPlugin.log(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "RepositoryProvider assigned to the project must be a subclass of RepositoryProvider: " + id, e));
-			return null;
+			TeamPlugin.log(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("RepositoryProviderTypeRepositoryProvider_assigned_to_the_project_must_be_a_subclass_of_RepositoryProvider___2") + id, e)); //$NON-NLS-1$
 		} catch(CoreException ex) {
 			// would happen if provider nature id is not registered with the resources plugin
-			TeamPlugin.log(new Status(IStatus.WARNING, TeamPlugin.ID, 0, "RepositoryProvider not registered as a nature id: " + id, ex));
-			return null;
+			TeamPlugin.log(new Status(IStatus.WARNING, TeamPlugin.ID, 0, Policy.bind("RepositoryProviderTypeRepositoryProvider_not_registered_as_a_nature_id___3") + id, ex)); //$NON-NLS-1$
 		}
+		return null;
 	}
 	
 	/**
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java
index 9757de1..d256ac3 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java
@@ -42,32 +42,29 @@
  * resource management plugin.
  * <p>
  * This plugin provides a lightweight registration and lookup service for
- * associating projects with providers. The registration mechanism is
- * based on the platform's project natures. Using projects natures allows
- * manipulating a project in a nature-specific way, for example UI 
- * contributions can be made conditional based on the nature of a project.
+ * managing global ignores patterns and file type associations. Also it 
+ * serves as a boot strap for registration of RepositoryProviderType classes.
  * </p>
  * 
- * @see ITeamNature
- * @see ITeamProvider
- * @see ITeamManager
+ * @see RepositoryProvider
+ * @see RepositoryProviderType
  */
 final public class TeamPlugin extends Plugin {
 
 	// The id of the core team plug-in
-	public static final String ID = "org.eclipse.team.core";
+	public static final String ID = "org.eclipse.team.core"; //$NON-NLS-1$
 
 	// The id of the providers extension point
-	private static final String PROVIDER_EXTENSION = "repository-provider-type";
+	private static final String PROVIDER_EXTENSION = "repository-provider-type"; //$NON-NLS-1$
 	
 	// The id of the file types extension point
-	private static final String FILE_TYPES_EXTENSION = "fileTypes";
+	private static final String FILE_TYPES_EXTENSION = "fileTypes"; //$NON-NLS-1$
 	
 	// The id of the global ignore extension point
-	private static final String IGNORE_EXTENSION = "ignore";
+	private static final String IGNORE_EXTENSION = "ignore"; //$NON-NLS-1$
 	
 	// File name of the persisted global ignore patterns
-	private final static String GLOBALIGNORE_FILE = ".globalIgnores";
+	private final static String GLOBALIGNORE_FILE = ".globalIgnores"; //$NON-NLS-1$
 	
 	// The ignore list that is read at startup from the persisted file
 	private static Map globalIgnore = new HashMap(11);
@@ -91,7 +88,7 @@
 	 */
 	public void startup() throws CoreException {
 		try {
-			Policy.localize("org.eclipse.team.core.internal.messages");						
+			Policy.localize("org.eclipse.team.core.internal.messages"); //$NON-NLS-1$
 			registry = new FileTypeRegistry();
 			registry.startup();
 			
@@ -179,7 +176,7 @@
 			// make sure that we update our state on disk
 			savePluginState();
 		} catch (TeamException ex) {
-			TeamPlugin.log(IStatus.WARNING, "setting global ignore", ex);
+			TeamPlugin.log(IStatus.WARNING, Policy.bind("TeamPlugin_setting_global_ignore_7"), ex); //$NON-NLS-1$
 		}
 	}
 	
@@ -196,7 +193,7 @@
 			description.setNatureIds(newNatures);
 			proj.setDescription(description, monitor);
 		} catch(CoreException e) {
-				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorSettingNature", 
+				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorSettingNature",  //$NON-NLS-1$
 														 proj.getName(), natureId), e));
 		}
 	}
@@ -213,7 +210,7 @@
 			description.setNatureIds((String[])newNatures.toArray(new String[newNatures.size()]));
 			proj.setDescription(description, monitor);
 		} catch(CoreException e) {
-				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorRemovingNature", 
+				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorRemovingNature",  //$NON-NLS-1$
 														 proj.getName(), natureId), e));
 		}
 	}
@@ -225,7 +222,7 @@
 
 		IExtensionPoint extensionPoint = Platform.getPluginRegistry().getExtensionPoint(TeamPlugin.ID, TeamPlugin.PROVIDER_EXTENSION);
 		if (extensionPoint == null) {
-			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.providerExtensionNotFound"), null));
+			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.providerExtensionNotFound"), null)); //$NON-NLS-1$
 		}
 
 		IExtension[] extensions = extensionPoint.getExtensions();
@@ -237,24 +234,24 @@
 			if (configs.length == 0) {
 				// there is no configuration element
 				// log as an error but continue to instantiate other executable extensions.
-				TeamPlugin.log(IStatus.ERROR, Policy.bind("manager.providerNoConfigElems", extension.getUniqueIdentifier()), null);
+				TeamPlugin.log(IStatus.ERROR, Policy.bind("manager.providerNoConfigElems", extension.getUniqueIdentifier()), null); //$NON-NLS-1$
 				continue;
 			}
 			IConfigurationElement config = configs[0];
 			String configName = config.getName();
-			if (!"repository".equals(config.getName())) {
-				String message = Policy.bind("resources.natureFormat", configName);
+			if (!"repository".equals(config.getName())) { //$NON-NLS-1$
+				String message = Policy.bind("resources.natureFormat", configName); //$NON-NLS-1$
 				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, message, null));
 			}
 
 			try {
-				RepositoryProviderType providerType = (RepositoryProviderType) config.createExecutableExtension("provider-type");
+				RepositoryProviderType providerType = (RepositoryProviderType) config.createExecutableExtension("provider-type"); //$NON-NLS-1$
 				RepositoryProviderType.addProviderType(providerType);
 			} catch (ClassCastException e) {
-				String message = Policy.bind("resources.natureImplement", configName);
+				String message = Policy.bind("resources.natureImplement", configName); //$NON-NLS-1$
 				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, message, null));
 			} catch (CoreException e){
-				String message = Policy.bind("resources.natureImplement", configName);
+				String message = Policy.bind("resources.natureImplement", configName); //$NON-NLS-1$
 				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, message, null));
 			}
 		}
@@ -272,10 +269,10 @@
 				for (int i = 0; i < extensions.length; i++) {
 					IConfigurationElement [] configElements = extensions[i].getConfigurationElements();
 					for (int j = 0; j < configElements.length; j++) {
-						String pattern = configElements[j].getAttribute("pattern");
+						String pattern = configElements[j].getAttribute("pattern"); //$NON-NLS-1$
 						if (pattern != null) {
-							String selected = configElements[j].getAttribute("selected");
-							boolean enabled = selected != null && selected.equalsIgnoreCase("true");
+							String selected = configElements[j].getAttribute("selected"); //$NON-NLS-1$
+							boolean enabled = selected != null && selected.equalsIgnoreCase("true"); //$NON-NLS-1$
 							// if this ignore doesn't already exist, add it to the global list
 							if (!globalIgnore.containsKey(pattern)) {
 								globalIgnore.put(pattern, new Boolean(enabled));
@@ -293,7 +290,7 @@
 	private void savePluginState() throws TeamException {
 		// save global ignore list to disk
 		IPath pluginStateLocation = TeamPlugin.getPlugin().getStateLocation();
-		File tempFile = pluginStateLocation.append(GLOBALIGNORE_FILE + ".tmp").toFile();
+		File tempFile = pluginStateLocation.append(GLOBALIGNORE_FILE + ".tmp").toFile(); //$NON-NLS-1$
 		File stateFile = pluginStateLocation.append(GLOBALIGNORE_FILE).toFile();
 		try {
 			DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
@@ -303,9 +300,9 @@
 				stateFile.delete();
 			boolean renamed = tempFile.renameTo(stateFile);
 			if (!renamed)
-				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "renaming", null));
+				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("TeamPlugin_renaming_21"), null)); //$NON-NLS-1$
 		} catch (IOException ex) {
-			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "closing stream", ex));
+			throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("TeamPlugin_closing_stream_22"), ex)); //$NON-NLS-1$
 		}
 	}
 	
@@ -349,7 +346,7 @@
 			} catch(FileNotFoundException e) {
 				// not a fatal error, there just happens not to be any state to read
 			} catch (IOException ex) {
-				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, "closing stream", ex));			
+				throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("TeamPlugin_closing_stream_23"), ex));			 //$NON-NLS-1$
 			}
 		}
 	}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileModificationValidator.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileModificationValidator.java
index 5359dc5..ebd3096 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileModificationValidator.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileModificationValidator.java
@@ -15,8 +15,8 @@
 import org.eclipse.team.core.TeamPlugin;
 
 public class FileModificationValidator implements IFileModificationValidator {
-	private static final Status OK = new Status(Status.OK, TeamPlugin.ID, Status.OK, Policy.bind("FileModificationValidator.ok"), null);
-	private static final Status READ_ONLY = new Status(Status.ERROR, TeamPlugin.ID, Status.ERROR, Policy.bind("FileModificationValidator.isReadOnly"), null);
+	private static final Status OK = new Status(Status.OK, TeamPlugin.ID, Status.OK, Policy.bind("FileModificationValidator.ok"), null); //$NON-NLS-1$
+	private static final Status READ_ONLY = new Status(Status.ERROR, TeamPlugin.ID, Status.ERROR, Policy.bind("FileModificationValidator.isReadOnly"), null); //$NON-NLS-1$
 	
 	/*
 	 * @see IFileModificationValidator#validateEdit(IFile[], Object)
@@ -45,7 +45,7 @@
 		if (result.length == 1) {
 			return result[0];
 		} 
-		return new MultiStatus(TeamPlugin.ID, 0, result, Policy.bind("FileModificationValidator.validateEdit"), null);
+		return new MultiStatus(TeamPlugin.ID, 0, result, Policy.bind("FileModificationValidator.validateEdit"), null); //$NON-NLS-1$
 	}
 
 	/*
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java
index 30cc05d..8abbd49 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java
@@ -28,10 +28,10 @@
  */
 public class FileTypeRegistry implements IFileTypeRegistry {
 	// Constant for the saved state file name
-	private static final String STATE_FILE = ".fileTypes";
+	private static final String STATE_FILE = ".fileTypes"; //$NON-NLS-1$
 	
 	// The id of the file types extension point
-	private static final String FILE_TYPES_EXTENSION = "fileTypes";
+	private static final String FILE_TYPES_EXTENSION = "fileTypes"; //$NON-NLS-1$
 
 	// Keys: file extensions. Values: Integers
 	private Hashtable table;
@@ -99,14 +99,14 @@
 				for (int i = 0; i < extensions.length; i++) {
 					IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
 					for (int j = 0; j < configElements.length; j++) {
-						String ext = configElements[j].getAttribute("extension");
+						String ext = configElements[j].getAttribute("extension"); //$NON-NLS-1$
 						if (ext != null) {
-							String type = configElements[j].getAttribute("type");
+							String type = configElements[j].getAttribute("type"); //$NON-NLS-1$
 							// If the extension doesn't already exist, add it.
 							if (!containsExtension(ext)) {
-								if (type.equals("text")) {
+								if (type.equals("text")) { //$NON-NLS-1$
 									setValue(ext, TEXT);
-								} else if (type.equals("binary")) {
+								} else if (type.equals("binary")) { //$NON-NLS-1$
 									setValue(ext, BINARY);
 								}
 							}
@@ -179,7 +179,7 @@
 	 */
 	private void savePluginState() {
 		IPath pluginStateLocation = TeamPlugin.getPlugin().getStateLocation();
-		File tempFile = pluginStateLocation.append(STATE_FILE + ".tmp").toFile();
+		File tempFile = pluginStateLocation.append(STATE_FILE + ".tmp").toFile(); //$NON-NLS-1$
 		File stateFile = pluginStateLocation.append(STATE_FILE).toFile();
 		try {
 			DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java
index f429d24..f6eda92 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java
@@ -50,7 +50,7 @@
 		} catch (MissingResourceException e) {
 			return key;
 		} catch (NullPointerException e) {
-			return "!" + key + "!";
+			return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 	
@@ -64,7 +64,7 @@
 		} catch (MissingResourceException e) {
 			return key;
 		} catch (NullPointerException e) {
-			return "!" + key + "!";
+			return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 	
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties
index 24861f0..47cd4df 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties
@@ -19,3 +19,13 @@
 FileModificationValidator.validateEdit=Validate Edit
 FileModificationValidator.isReadOnly=File is Read Only.
 FileModificationValidator.ok=OK
+
+RepositoryProvider_Error_removing_nature_from_project___1=Error removing nature from project: 
+RepositoryProvider_Too_many_providers_associated_with_project___2=Too many providers associated with project: 
+RepositoryProviderTypeduplicate_provider_found_in_plugin.xml___1=duplicate provider found in plugin.xml: 
+RepositoryProviderTypeRepositoryProvider_assigned_to_the_project_must_be_a_subclass_of_RepositoryProvider___2=RepositoryProvider assigned to the project must be a subclass of RepositoryProvider: 
+RepositoryProviderTypeRepositoryProvider_not_registered_as_a_nature_id___3=RepositoryProvider not registered as a nature id: 
+TeamPlugin_setting_global_ignore_7=setting global ignore
+TeamPlugin_renaming_21=renaming
+TeamPlugin_closing_stream_22=closing stream
+TeamPlugin_closing_stream_23=closing stream
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/Assert.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/Assert.java
index 6af2dbd..0f19040 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/Assert.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/Assert.java
@@ -28,7 +28,7 @@
  * @exception IllegalArgumentException if the legality test failed
  */
 public static boolean isLegal(boolean expression) {
-	return isLegal(expression, ""/*nonNLS*/);
+	return isLegal(expression, ""); //$NON-NLS-1$
 }
 /** Asserts that an argument is legal. If the given boolean is
  * not <code>true</code>, an <code>IllegalArgumentException</code>
@@ -53,7 +53,7 @@
  * @exception IllegalArgumentException if the object is <code>null</code>
  */
 public static void isNotNull(Object object) {
-	isNotNull(object, ""/*nonNLS*/);
+	isNotNull(object, ""/*nonNLS*/); //$NON-NLS-1$
 }
 /** Asserts that the given object is not <code>null</code>. If this
  * is not the case, some kind of unchecked exception is thrown.
@@ -65,7 +65,7 @@
  */
 public static void isNotNull(Object object, String message) {
 	if (object == null)
-		throw new AssertionFailedException("null argument:" /*non NLS*/ + message);
+		throw new AssertionFailedException("null argument:" /*non NLS*/ + message); //$NON-NLS-1$
 }
 /** Asserts that the given boolean is <code>true</code>. If this
  * is not the case, some kind of unchecked exception is thrown.
@@ -75,7 +75,7 @@
  *    if the check fails)
  */
 public static boolean isTrue(boolean expression) {
-	return isTrue(expression, ""/*nonNLS*/);
+	return isTrue(expression, ""/*nonNLS*/); //$NON-NLS-1$
 }
 /** Asserts that the given boolean is <code>true</code>. If this
  * is not the case, some kind of unchecked exception is thrown.
@@ -88,7 +88,7 @@
  */
 public static boolean isTrue(boolean expression, String message) {
 	if (!expression)
-		throw new AssertionFailedException("assert failed:" /*non NLS*/ + message);
+		throw new AssertionFailedException("assert failed:" /*non NLS*/ + message); //$NON-NLS-1$
 	return expression;
 }
 /**
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteSyncElement.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteSyncElement.java
index 571779e..3ffd4be 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteSyncElement.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteSyncElement.java
@@ -368,25 +368,25 @@
 	 */
 	public String toString() {
 		StringBuffer buffer = new StringBuffer();
-		buffer.append(getName() + "[");
+		buffer.append(getName() + "["); //$NON-NLS-1$
 		int kind = getSyncKind(GRANULARITY_TIMESTAMP, null);
 		if(kind==IN_SYNC) {
-			buffer.append("in-sync");
+			buffer.append("in-sync"); //$NON-NLS-1$
 		} else {
 			switch(kind & DIRECTION_MASK) {
-				case CONFLICTING: buffer.append("conflicting"); break;
-				case OUTGOING: buffer.append("outgoing"); break;
-				case INCOMING: buffer.append("incoming"); break;
+				case CONFLICTING: buffer.append("conflicting"); break; //$NON-NLS-1$
+				case OUTGOING: buffer.append("outgoing"); break; //$NON-NLS-1$
+				case INCOMING: buffer.append("incoming"); break; //$NON-NLS-1$
 			}		
 			switch(kind & DIRECTION_MASK) {
-				case CHANGE: buffer.append("change"); break;
-				case ADDITION: buffer.append("addition"); break;
-				case DELETION: buffer.append("deletion"); break;
+				case CHANGE: buffer.append("change"); break; //$NON-NLS-1$
+				case ADDITION: buffer.append("addition"); break; //$NON-NLS-1$
+				case DELETION: buffer.append("deletion"); break; //$NON-NLS-1$
 			}
-			if((kind & MANUAL_CONFLICT) != 0) buffer.append("{manual}");
-			if((kind & AUTOMERGE_CONFLICT) != 0) buffer.append("{auto}");
+			if((kind & MANUAL_CONFLICT) != 0) buffer.append("{manual}"); //$NON-NLS-1$
+			if((kind & AUTOMERGE_CONFLICT) != 0) buffer.append("{auto}"); //$NON-NLS-1$
 		}
-		buffer.append("]");
+		buffer.append("]"); //$NON-NLS-1$
 		return buffer.toString();
 	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProvider.java
index 6298f04..c72db56 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProvider.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProvider.java
@@ -582,7 +582,7 @@
 			IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
 			for (int i = 0; i < projects.length; i++) {
 				RepositoryProvider provider = RepositoryProviderType.getProvider(projects[i]);
-				if (provider.isOfType(CVSProviderPlugin.getTypeId())) {
+				if (provider!=null && provider.isOfType(CVSProviderPlugin.getTypeId())) {
 					ICVSFolder folder = (ICVSFolder)CVSWorkspaceRoot.getCVSResourceFor(projects[i]);
 					FolderSyncInfo info = folder.getFolderSyncInfo();
 					if (info != null) {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
index b5fe9d0..d673752 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
@@ -202,7 +202,7 @@
 							IProject project = (IProject)resource;
 							// Get the team provider for the project and
 							RepositoryProvider provider = RepositoryProviderType.getProvider(project);
-							if(!provider.isOfType(NATURE_ID)) continue;
+							if(provider==null || !provider.isOfType(NATURE_ID)) continue;
 							/* Check if the project description changed. */
 							if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
 								/* The project description changed. Write the file. */
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
index 445f128..f4bb686 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
@@ -5,12 +5,18 @@
  * All Rights Reserved.
  */
  
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
-import java.util.Properties;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -40,8 +46,11 @@
 import org.eclipse.team.internal.ccvs.core.client.Session;
 import org.eclipse.team.internal.ccvs.core.client.Tag;
 import org.eclipse.team.internal.ccvs.core.client.Update;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
+import org.eclipse.team.internal.ccvs.core.client.listeners.AdminKSubstListener;
 import org.eclipse.team.internal.ccvs.core.client.listeners.DiffListener;
+import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
 import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
 import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
 import org.eclipse.team.internal.ccvs.core.resources.CVSRemoteSyncElement;
@@ -80,7 +89,13 @@
  * have them appear in Eclipse. This may be changed in the future.
  */
 public class CVSTeamProvider extends RepositoryProvider {
-
+	
+	private static final int CR_BYTE = 0x0D;
+	private static final int LF_BYTE = 0x0A;
+	private static final boolean IS_CRLF_PLATFORM = Arrays.equals(
+		System.getProperty("line.separator").getBytes(),
+		new byte[] { CR_BYTE, LF_BYTE });
+	
 	private CVSWorkspaceRoot workspaceRoot;
 	private IProject project;
 	private String comment = "";  //$NON-NLS-1$
@@ -639,7 +654,7 @@
 			CVSProvider.getInstance().addRepository(newLocation);
 			
 			// Set the project to use the new Locations
-			setRemoteRoot(newLocation, Policy.infiniteSubMonitorFor(monitor, 80));
+			setRemoteRoot(newLocation, Policy.subMonitorFor(monitor, 80));
 			return true;
 		} finally {
 			monitor.done();
@@ -886,30 +901,40 @@
 	/*
 	 * This method expects to be passed an InfiniteSubProgressMonitor
 	 */
-	private void setRemoteRoot(ICVSRepositoryLocation location, final IProgressMonitor monitor) throws TeamException {
+	private void setRemoteRoot(ICVSRepositoryLocation location, IProgressMonitor monitor) throws TeamException {
 
 		// Check if there is a differnece between the new and old roots	
 		final String root = location.getLocation();
 		if (root.equals(workspaceRoot.getRemoteLocation())) 
 			return;
-				
+	
 		try {
-			// 256 ticks gives us a maximum of 1024 which seems reasonable for folders is a project
-			monitor.beginTask(Policy.bind("CVSTeamProvider.folderInfo", project.getName()), 256);  //$NON-NLS-1$
-			
-			// Visit all the children folders in order to set the root in the folder sync info
-			workspaceRoot.getLocalRoot().accept(new ICVSResourceVisitor() {
-				public void visitFile(ICVSFile file) throws CVSException {};
-				public void visitFolder(ICVSFolder folder) throws CVSException {
-					monitor.worked(1);
-					FolderSyncInfo info = folder.getFolderSyncInfo();
-					if (info != null) {
-						monitor.subTask(Policy.bind("CVSTeamProvider.updatingFolder", info.getRepository())); //$NON-NLS-1$
-						folder.setFolderSyncInfo(new FolderSyncInfo(info.getRepository(), root, info.getTag(), info.getIsStatic()));
-						folder.acceptChildren(this);
+			workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
+				public void run(IProgressMonitor progress) throws CVSException {
+					try {
+						// 256 ticks gives us a maximum of 1024 which seems reasonable for folders is a project
+						progress.beginTask(null, 100);
+						final IProgressMonitor monitor = Policy.infiniteSubMonitorFor(progress, 100);
+						monitor.beginTask(Policy.bind("CVSTeamProvider.folderInfo", project.getName()), 256);  //$NON-NLS-1$
+		
+						// Visit all the children folders in order to set the root in the folder sync info
+						workspaceRoot.getLocalRoot().accept(new ICVSResourceVisitor() {
+							public void visitFile(ICVSFile file) throws CVSException {};
+							public void visitFolder(ICVSFolder folder) throws CVSException {
+								monitor.worked(1);
+								FolderSyncInfo info = folder.getFolderSyncInfo();
+								if (info != null) {
+									monitor.subTask(Policy.bind("CVSTeamProvider.updatingFolder", info.getRepository())); //$NON-NLS-1$
+									folder.setFolderSyncInfo(new FolderSyncInfo(info.getRepository(), root, info.getTag(), info.getIsStatic()));
+									folder.acceptChildren(this);
+								}
+							};
+						});
+					} finally {
+						progress.done();
 					}
-				};
-			});
+				}
+			}, monitor);
 		} finally {
 			monitor.done();
 		}
@@ -943,4 +968,226 @@
 	public RepositoryProviderType getProviderType() {
 		return CVSRepositoryProviderType.getInstance();
 	}
+	
+	/**
+	 * Fixes the line delimiters in the local file to reflect the platform's
+	 * native encoding.  Performs CR/LF -> LF or LF -> CR/LF conversion
+	 * depending on the platform but does not affect delimiters that are
+	 * already correctly encoded.
+	 */
+	public static void cleanLineDelimiters(IFile file, boolean useCRLF, IProgressMonitor progress)
+		throws CVSException {
+		try {
+			// convert delimiters in memory
+			boolean changed = false;
+			InputStream is = null;
+			ByteArrayOutputStream bos = new ByteArrayOutputStream();
+			try {
+				is = new BufferedInputStream(file.getContents());
+				boolean seenCR = false;
+				int c;
+				while ((c = is.read()) != -1) {
+					if (c == LF_BYTE) {
+						if (useCRLF) {
+							bos.write(CR_BYTE);
+							if (! seenCR) changed = true; // added CR
+						} else {
+							if (seenCR) changed = true; // stripped CR
+						}
+						bos.write(LF_BYTE);
+						seenCR = false;
+					} else {
+						if (seenCR) {
+							bos.write(CR_BYTE); // preserve orphaned carriage returns
+							seenCR = false;
+						}
+						if (c == CR_BYTE) {
+							seenCR = true;
+						} else {
+							bos.write(c);
+						}
+					}
+				}
+				if (seenCR) {
+					bos.write(CR_BYTE); // preserve orphaned carriage returns
+				}
+			} finally {
+				if (is != null) is.close();
+				bos.close();
+			}
+			if (changed) {
+				// write file back to disk with corrected delimiters if changes were made
+				ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+				file.setContents(bis, false /*force*/, true /*keepHistory*/, progress);
+			} else {
+				// otherwise make the file dirty
+				makeDirty(file);
+			}
+		} catch (CoreException e) {
+			throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e);
+		} catch (IOException e) {
+			throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e);
+		}
+	}
+	
+		/**
+	 * Sets the keyword substitution mode for the specified resources.
+	 * <p>
+	 * Applies the following rules in order:<br>
+	 * <ul>
+	 *   <li>If a file is not managed, skips it.</li>
+	 *   <li>If a file is not changing modes, skips it.</li>
+	 *   <li>If a file is being changed from binary to text, corrects line delimiters
+	 *       then commits it, then admins it.</li>
+	 *   <li>If a file is added, changes the resource sync information locally.</li>
+	 *   <li>Otherwise commits the file (with FORCE to create a new revision), then admins it.</li>
+	 * </ul>
+	 * All files that are admin'd are committed with FORCE to prevent other developers from
+	 * casually trying to commit pending changes to the repository without first checking out
+	 * a new copy.  This is not a perfect solution, as they could just as easily do an UPDATE
+	 * and not obtain the new keyword sync info.
+	 * </p>
+	 * 
+	 * @param resources the resources to set keyword substitution mode
+	 * @param depth the recursion depth
+	 * @param toKSubst the desired keyword substitution mode
+	 * @param monitor the progress monitor
+	 * @return a status code indicating success or failure of the operation
+	 * 
+	 * @throws TeamException
+	 */
+	public IStatus setKeywordSubstitution(final IResource[] resources, final int depth,
+		final KSubstOption toKSubst, IProgressMonitor monitor) throws TeamException {
+		final IStatus[] result = new IStatus[] { ICommandOutputListener.OK };
+		workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
+			public void run(final IProgressMonitor monitor) throws CVSException {
+				final boolean toBinary = toKSubst.isBinary();
+				final List /* of String */ filesToAdmin = new ArrayList();
+				final List /* of String */ filesToCommit = new ArrayList();
+				final Collection /* of ICVSFile */ filesToCommitAsText = new HashSet(); // need fast lookup
+		
+				final IProgressMonitor progress = Policy.monitorFor(monitor);
+				progress.beginTask(Policy.bind("CVSTeamProvider.preparingToSetKSubst"), 100);
+				try {
+					/*** get all possibly affected files (ensure no duplicates) ***/
+					final Set /* of IFile */ files = new HashSet();
+					for (int i = 0; i < resources.length; i++) {
+						final IResource currentResource = resources[i];
+						// throw an exception if the resource is not a child of the receiver
+						checkIsChild(currentResource);
+						try {
+							currentResource.accept(new IResourceVisitor() {
+								public boolean visit(IResource resource) throws CoreException {
+									if (resource.getType() == IResource.FILE) {
+										files.add(resource);
+									}
+									// always return true and let the depth determine if children are visited
+									return true;
+								}
+							}, depth, false);
+						} catch (CoreException e) {
+							throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID,
+								TeamException.UNABLE, Policy.bind("CVSTeamProvider.visitError", //$NON-NLS-1$
+								new Object[] { currentResource.getFullPath() }), e));
+						}
+					}
+					progress.worked(5);
+		
+					/*** determine the resources to be committed and those to be admin'd ***/
+					for (Iterator it = files.iterator(); it.hasNext();) {
+						IFile file = (IFile) it.next();
+						ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+						// only set keyword substitution if resource is a managed file
+						if (mFile.isManaged()) {
+							ResourceSyncInfo info = mFile.getSyncInfo();
+							String fromMode = info.getKeywordMode();
+							KSubstOption fromKSubst = KSubstOption.fromMode(fromMode);
+		
+							// make sure we commit all added or changed resources
+							String remotePath = mFile.getRelativePath(workspaceRoot.getLocalRoot());
+		
+							// check if mode must be changed
+							if (! toKSubst.equals(fromKSubst)) {
+								if (info.isAdded()) {
+									// change resource sync info for outgoing addition
+									ResourceSyncInfo newInfo = new ResourceSyncInfo(
+										info.getName(), info.getRevision(), info.getTimeStamp(), toKSubst.toMode(),
+										info.getTag(), info.getPermissions());
+									mFile.setSyncInfo(newInfo);
+								} else if (info.isDeleted()) {
+									// ignore deletions
+								} else {
+									// file exists remotely
+									boolean fromBinary = fromKSubst.isBinary();
+									if (fromBinary && ! toBinary) {
+										// converting from binary to text
+										cleanLineDelimiters(file, IS_CRLF_PLATFORM, progress);
+										// remember to commit the cleaned resource as text before admin
+										filesToCommitAsText.add(mFile);
+									} else {
+										// force a commit to bump the revision number
+										makeDirty(file);
+									}
+									// remember to commit and admin the resource
+									filesToCommit.add(remotePath); // FORCE creation of a new revision
+									filesToAdmin.add(remotePath);
+								}
+							}
+						}
+					}
+					progress.worked(5);
+			
+					/*** commit then admin the resources ***/
+					if (filesToAdmin.size() != 0 || filesToCommit.size() != 0) {
+						Session s = new Session(workspaceRoot.getRemoteLocation(),
+							workspaceRoot.getLocalRoot(), false);
+						IProgressMonitor sessionProgress = Policy.subMonitorFor(progress, 90);
+						sessionProgress.beginTask(Policy.bind("CVSTeamProvider.settingKSubst"), 5 +
+							filesToAdmin.size() + filesToCommit.size());
+						try {
+							s.open(Policy.subMonitorFor(sessionProgress, 5));
+							
+							// commit files that changed from binary to text
+							// NOTE: The files are committed as text with conversions even if the
+							//       resource sync info still says "binary".
+							if (filesToCommit.size() != 0) {
+								s.setTextTransferOverride(filesToCommitAsText);
+								result[0] = Command.COMMIT.execute(s, Command.NO_GLOBAL_OPTIONS,
+									new LocalOption[] { Commit.DO_NOT_RECURSE, Commit.FORCE,
+										Commit.makeArgumentOption(Command.MESSAGE_OPTION, comment) },
+									(String[]) filesToCommit.toArray(new String[filesToCommit.size()]),
+									null, Policy.subMonitorFor(sessionProgress, filesToCommit.size()));
+								s.setTextTransferOverride(null);
+								// if errors were encountered, abort
+								if (! result[0].isOK()) return;
+							}
+							
+							// admin files that changed keyword substitution mode
+							// NOTE: As confirmation of the completion of a command, the server replies
+							//       with the RCS command output if a change took place.  Rather than
+							//       assume that the command succeeded, we listen for these lines
+							//       and update the local ResourceSyncInfo for the particular files that
+							//       were actually changed remotely.
+							result[0] = Command.ADMIN.execute(s, Command.NO_GLOBAL_OPTIONS,
+								new LocalOption[] { toKSubst },
+								(String[]) filesToAdmin.toArray(new String[filesToAdmin.size()]),
+								new AdminKSubstListener(toKSubst.toMode()),
+								Policy.subMonitorFor(sessionProgress, filesToAdmin.size()));
+						} finally {
+							s.close();
+							sessionProgress.done();
+						}
+					}
+				} finally {
+					progress.done();
+				}
+			}
+		}, monitor);
+		return result[0];
+	}
+	
+	private static void makeDirty(IFile file) throws CVSException {
+		ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+		mFile.setTimeStamp(null);
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
index 58429dd..43c02c6 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/AbstractStructureVisitor.java
@@ -18,6 +18,7 @@
 import org.eclipse.team.ccvs.core.ICVSResourceVisitor;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.resources.CVSEntryLineTag;
 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
@@ -149,7 +150,7 @@
 		// If the file exists, send the appropriate indication to the server
 		if (mFile.exists()) {
 			if (mFile.isModified()) {
-				boolean binary = (info != null) && ResourceSyncInfo.BINARY_TAG.equals(mFile.getSyncInfo().getKeywordMode());
+				boolean binary = info != null && KSubstOption.fromMode(info.getKeywordMode()).isBinary();
 				if (sendModifiedContents) {
 					session.sendModified(mFile, binary, monitor);
 				} else {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
index 2b2005f..5a23711 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Command.java
@@ -7,9 +7,11 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 import org.eclipse.core.runtime.IProgressMonitor;
@@ -22,6 +24,7 @@
 import org.eclipse.team.ccvs.core.ICVSResource;
 import org.eclipse.team.ccvs.core.ICVSRunnable;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProvider;
 import org.eclipse.team.internal.ccvs.core.Policy;
 import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
 
@@ -81,13 +84,21 @@
 	public static final LocalOption[] NO_LOCAL_OPTIONS = new LocalOption[0];
 	// valid for: annotate checkout commit diff export log rdiff remove rtag status tag update  
 	public static final LocalOption DO_NOT_RECURSE = new LocalOption("-l"); //$NON-NLS-1$	
-	// valid for: add checkout export import update
-	public static final LocalOption KSUBST_BINARY = new LocalOption("-kb"); //$NON-NLS-1$
 	// valid for: checkout export update
 	public static final LocalOption PRUNE_EMPTY_DIRECTORIES = new LocalOption("-P"); //$NON-NLS-1$
 	// valid for: checkout export update
 	public static final LocalOption MESSAGE_OPTION = new LocalOption("-m"); //$NON-NLS-1$
 
+	/*** Local options: keyword substitution mode ***/
+	// valid for: add admin checkout export import update
+	private static final Map ksubstOptionMap = new HashMap();
+	public static final KSubstOption KSUBST_BINARY = new KSubstOption("-kb"); //$NON-NLS-1$
+	public static final KSubstOption KSUBST_TEXT = new KSubstOption("-ko"); //$NON-NLS-1$
+	public static final KSubstOption KSUBST_TEXT_EXPAND = new KSubstOption("-kkv"); //$NON-NLS-1$
+	public static final KSubstOption KSUBST_TEXT_EXPAND_LOCKER = new KSubstOption("-kkvl"); //$NON-NLS-1$
+	public static final KSubstOption KSUBST_TEXT_VALUES_ONLY = new KSubstOption("-kv"); //$NON-NLS-1$
+	public static final KSubstOption KSUBST_TEXT_KEYWORDS_ONLY = new KSubstOption("-kk"); //$NON-NLS-1$
+
 	/*** Response handler map ***/
 	private static final Hashtable responseHandlers = new Hashtable();
 	static {
@@ -541,14 +552,36 @@
 		/**
 		 * Returns the option part of the option
 		 */
-		public String getOption() {
+		String getOption() {
 			return option;
 		}
 		/**
+		 * Compares two options for equality.
+		 * @param other the other option
+		 */
+		public boolean equals(Object other) {
+			if (this == other) return true;
+			if (other instanceof Option) {
+				Option otherOption = (Option) other;
+				return option.equals(otherOption.option);
+			}
+			return false;
+		}
+		/**
 		 * Sends the option to a CVS server
 		 * @param session the CVS session
 		 */
 		public abstract void send(Session session) throws CVSException;		
+		/*
+		 * To make debugging a tad easier.
+		 */
+		public String toString() {
+			if (argument != null && argument.length() != 0) {
+				return option + " " + argument;
+			} else {
+				return option;
+			}
+		}
 	}	
 	/**
 	 * Option subtype for global options that are common to all commands.
@@ -587,6 +620,83 @@
 			if (argument != null) session.sendArgument(argument);
 		}
 	}
+	/**
+	 * Options subtype for keyword substitution options.
+	 */
+	public static class KSubstOption extends LocalOption {
+		private String shortDisplayText;
+		private String longDisplayText;
+		
+		private KSubstOption(String option) {
+			this(option, Policy.bind("KSubstOption." + option + ".short"),
+				Policy.bind("KSubstOption." + option + ".long"));
+		}
+		private KSubstOption(String option, String shortDisplayText, String longDisplayText) {
+			super(option);
+			this.shortDisplayText = shortDisplayText;
+			this.longDisplayText = longDisplayText;
+			ksubstOptionMap.put(option, this);
+		}
+		/**
+		 * Gets the KSubstOption instance for the specified mode.
+		 * 
+		 * @param mode the mode, e.g. -kb
+		 * @return an instance for that mode
+		 */
+		public static KSubstOption fromMode(String mode) {
+			if (mode.length() == 0) mode = "-kkv"; // use default
+			KSubstOption option = (KSubstOption) ksubstOptionMap.get(mode);
+			if (option == null) {
+				option = new KSubstOption(mode,
+					Policy.bind("KSubstOption.unknown.short", mode),
+					Policy.bind("KSubstOption.unknown.long", mode));
+				ksubstOptionMap.put(mode, option);
+			}
+			return option;
+		}
+		/**
+		 * Gets the KSubstOption instance for the specified file by pattern.
+		 *
+		 * @param filename the filename of interest
+		 * @return an instance for that mode
+		 */
+		public static KSubstOption fromPattern(String filename) {
+			if (CVSProvider.isText(filename)) return KSUBST_TEXT_EXPAND; // XXX should this be KSUBST_TEXT?
+			return KSUBST_BINARY;
+		}
+		/**
+		 * Returns an array of all valid modes.
+		 */
+		public static KSubstOption[] getAllKSubstOptions() {
+			return (KSubstOption[]) ksubstOptionMap.values().toArray(new KSubstOption[ksubstOptionMap.size()]);
+		}
+		/**
+		 * Returns the entry line mode string for this instance.
+		 */
+		public String toMode() {
+			if (KSUBST_TEXT_EXPAND.equals(this)) return "";
+			return getOption();
+		}
+		/**
+		 * Returns true if the substitution mode requires no data translation
+		 * during file transfer.
+		 */
+		public boolean isBinary() {
+			return KSUBST_BINARY.equals(this);
+		}
+		/**
+		 * Returns a short localized text string describing this mode.
+		 */
+		public String getShortDisplayText() {
+			return shortDisplayText;
+		}
+		/**
+		 * Returns a long localized text string describing this mode.
+		 */
+		public String getLongDisplayText() {
+			return longDisplayText;
+		}
+	}
 
 	/**
 	 * Makes a -m log message option.
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
index 6545fe5..e1e59dc 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Commit.java
@@ -14,6 +14,12 @@
 
 public class Commit extends Command {
 	/*** Local options: specific to commit ***/
+	// Forces a file to be committed even if it has not been modified; implies -l.
+	// NOTE: This option is not fully supported -- a file will not be sent
+	//       unless it is dirty.  The primary use is to resend a file that may
+	//       or may not be changed (e.g. could depend on CR/LF translations, etc...)
+	//       and force the server to create a new revision and reply Checked-in.
+	public static final LocalOption FORCE = new LocalOption("-f");
 
 	protected Commit() { }
 	protected String getCommandId() {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
index 524f317..15a9a12 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/DiffStructureVisitor.java
@@ -6,6 +6,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.team.ccvs.core.ICVSFile;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 
 /**
@@ -24,7 +25,7 @@
 	 * Send unmanaged files as modified with a default entry line.
 	 */
 	protected void sendFile(ICVSFile mFile, String mode) throws CVSException {
-		boolean binary = mode != null && mode.indexOf(ResourceSyncInfo.BINARY_TAG) != -1;
+		boolean binary = mode != null && KSubstOption.fromMode(mode).isBinary();
 		boolean newFile = false;
 
 		if (mFile.isManaged()) {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
index 702dc33..3aece0b 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/ImportStructureVisitor.java
@@ -14,7 +14,7 @@
 import org.eclipse.team.ccvs.core.ICVSResourceVisitor;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.Policy;
-import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.util.FileNameMatcher;
 
 /**
@@ -105,14 +105,12 @@
 			return;
 		}
 		
-		String mode;
+		// XXX should we default to text or to binary?
+		boolean binary = false;
 		if (wrapMatcher != null) {
-			mode = wrapMatcher.getMatch(mFile.getName());
-		} else {
-			mode = ""; //$NON-NLS-1$
+			String mode = wrapMatcher.getMatch(mFile.getName());
+			if (mode != null) binary = KSubstOption.fromMode(mode).isBinary();
 		}
-		// XXX Is this condition right?
-		boolean binary = mode != null && mode.indexOf(ResourceSyncInfo.BINARY_TAG) != -1;
 		session.sendModified(mFile, binary, monitor);
 	}
 
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
index bdd8569..b492a3c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
@@ -10,6 +10,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 
@@ -78,6 +79,7 @@
 	private Date modTime = null;
 	private boolean noLocalChanges = false;
 	private List expansions;
+	private Collection /* of ICVSFile */ textTransferOverrideSet = null;
 
 	// a shared buffer used for file transfers
 	private byte[] transferBuffer = null;
@@ -510,6 +512,10 @@
 	 */
 	public void sendFile(ICVSFile file, boolean isBinary, IProgressMonitor monitor)
 		throws CVSException {
+		// check overrides
+		if (textTransferOverrideSet != null &&
+			textTransferOverrideSet.contains(file)) isBinary = false;
+
 		// update progress monitor
 		String title = Policy.bind(getSendFileTitleKey(), new Object[]{ Util.toTruncatedPath(file, localRoot, 3) }); //$NON-NLS-1$
 		monitor.subTask(Policy.bind("Session.transferNoSize", title)); //$NON-NLS-1$
@@ -605,6 +611,10 @@
 	 */
 	public void receiveFile(ICVSFile file, boolean isBinary, int responseType, IProgressMonitor monitor)
 	throws CVSException {
+		// check overrides
+		if (textTransferOverrideSet != null &&
+			textTransferOverrideSet.contains(file)) isBinary = false;
+
 		// update progress monitor
 		String title = Policy.bind("Session.receiving", new Object[]{ Util.toTruncatedPath(file, localRoot, 3) }); //$NON-NLS-1$
 		monitor.subTask(Policy.bind("Session.transferNoSize", title)); //$NON-NLS-1$
@@ -780,5 +790,14 @@
 	public void setSendFileTitleKey(String sendFileTitleKey) {
 		this.sendFileTitleKey = sendFileTitleKey;
 	}
-
+	
+	/**
+	 * Remembers a set of files that must be transferred as 'text'
+	 * regardless of what the isBinary parameter to sendFile() is.
+	 * 
+	 * @param textTransferOverrideSet the set of ICVSFiles to override, or null if none
+	 */
+	public void setTextTransferOverride(Collection textTransferOverrideSet) {
+		this.textTransferOverrideSet = textTransferOverrideSet;
+	}
 }
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
index 97137fa..f6a131e 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/TagFileSender.java
@@ -8,6 +8,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.team.ccvs.core.ICVSFile;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 
 /**
@@ -35,7 +36,7 @@
 			}
 			if (! info.isAdded()) {
 				session.sendEntry(info.getEntryLine(false, mFile.getTimeStamp()));
-				boolean binary = (info != null) && ResourceSyncInfo.BINARY_TAG.equals(mFile.getSyncInfo().getKeywordMode());
+				boolean binary = info != null && KSubstOption.fromMode(info.getKeywordMode()).isBinary();
 				session.sendIsModified(mFile, binary, monitor);
 			}
 		}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
index ef4b4fb..320b18a 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/UpdatedHandler.java
@@ -11,6 +11,7 @@
 import org.eclipse.team.ccvs.core.ICVSFile;
 import org.eclipse.team.ccvs.core.ICVSFolder;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 import org.eclipse.team.internal.ccvs.core.util.Assert;
 import org.eclipse.team.internal.ccvs.core.util.EntryFileDateFormat;
@@ -71,6 +72,7 @@
 		String repositoryFile = session.readLine();
 		String entryLine = session.readLine();
 		String permissionsLine = session.readLine();
+		ResourceSyncInfo info = new ResourceSyncInfo(entryLine, permissionsLine, null);
 
 		// clear file update modifiers
 		Date modTime = session.getModTime();
@@ -83,8 +85,8 @@
 		Assert.isTrue(mParent.exists());
 		ICVSFile mFile = mParent.getFile(fileName);
 		
-		boolean binary = entryLine.indexOf("/" + ResourceSyncInfo.BINARY_TAG) != -1; //$NON-NLS-1$
-		boolean readOnly = permissionsLine.indexOf(READ_ONLY_FLAG) == -1;
+		boolean binary = KSubstOption.fromMode(info.getKeywordMode()).isBinary();
+		boolean readOnly = info.getPermissions().indexOf(READ_ONLY_FLAG) == -1;
 		
 		session.receiveFile(mFile, binary, handlerType, monitor);
 		if (readOnly) mFile.setReadOnly(true);
@@ -101,14 +103,15 @@
 			// having outgoing changes.
 			// The purpose for having the two different timestamp options for merges is to 
 			// dissallow commit of files that have conflicts until they have been manually edited.			
-			if(entryLine.indexOf(ResourceSyncInfo.MERGE_UNMODIFIED) != -1) {
+			if(info.getTimeStamp().indexOf(ResourceSyncInfo.MERGE_UNMODIFIED) != -1) {
 				timestamp = ResourceSyncInfo.RESULT_OF_MERGE_CONFLICT + mFile.getTimeStamp();
 			} else {
 				timestamp = ResourceSyncInfo.RESULT_OF_MERGE;
 			}
 		} else {
 			timestamp = mFile.getTimeStamp();
-		}				
-		mFile.setSyncInfo(new ResourceSyncInfo(entryLine, permissionsLine, timestamp));
+		}
+		mFile.setSyncInfo(new ResourceSyncInfo(info.getName(), info.getRevision(),
+			timestamp, info.getKeywordMode(), info.getTag(), info.getPermissions()));
 	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
index 124a71b..d5b23d3 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
@@ -68,6 +68,9 @@
 CVSTeamProvider.scrubbingResource=Scrubbing {0}
 CVSTeamProvider.updatingFile=Updating {0}
 CVSTeamProvider.makeBranch=Creating branch
+CVSTeamProvider.preparingToSetKSubst=Preparing to set keyword substitution mode
+CVSTeamProvider.settingKSubst=Setting keyword substitution mode
+CVSTeamProvider.cleanLineDelimitersException=Exception occurred while cleaning line delimiters
 
 ProjectDescriptionManager.unableToSetDescription=An error occured setting the project description
 ProjectDescriptionManager.unableToReadDescription=An error occured reading the project description
@@ -138,6 +141,26 @@
 UnsupportedHandler.message=Unsupported response received from server
 RemovedHandler.invalid=Invalid removed response received from CVS server for  {0}
 
+KSubstOption.-kb.short=Binary
+KSubstOption.-kb.long=Binary (-kb)
+KSubstOption.-ko.short=Text
+KSubstOption.-ko.long=Text (-ko)
+KSubstOption.-kkv.short=Text -kkv
+KSubstOption.-kkv.long=Text with keyword expansion (-kkv)
+KSubstOption.-kkvl.short=Text -kkvl
+KSubstOption.-kkvl.long=Text with keyword expansion and locker (-kkvl)
+KSubstOption.-kv.short=Text -kv
+KSubstOption.-kv.long=Text with keyword replacement (-kv)
+KSubstOption.-kk.short=Text -kk
+KSubstOption.-kk.long=Text with keyword compression (-kk)
+KSubstOption.unknown.short=Unknown {0}
+KSubstOption.unknown.long=Unknown ({0})
+
+AdminKSubstListener.expectedRCSFile=Expected RCS file {0} to end in ',v'
+AdminKSubstListener.commandRootNotManaged=Local root for this command is not managed
+AdminKSubstListener.expectedChildOfCommandRoot=Expected RCS file {0} to be a child of remote root for this command {1}
+AdminKSubstListener.couldNotSetResourceSyncInfo=Could not set resource sync info for {0}: {1}
+
 CVSRepositoryLocation.nullLocation=Location must not be null
 CVSRepositoryLocation.emptyLocation=Location must not be empty
 CVSRepositoryLocation.endWhitespace=Location must not end with whitespace
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
index 8708710..26f1d17 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSRemoteSyncElement.java
@@ -18,9 +18,9 @@
 import org.eclipse.team.core.sync.IRemoteSyncElement;
 import org.eclipse.team.core.sync.RemoteSyncElement;
 import org.eclipse.team.internal.ccvs.core.CVSException;
-import org.eclipse.team.internal.ccvs.core.CVSProvider;
 import org.eclipse.team.internal.ccvs.core.Policy;
 import org.eclipse.team.internal.ccvs.core.client.Update;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 import org.eclipse.team.internal.ccvs.core.util.Assert;
@@ -159,8 +159,9 @@
 			if (local.exists()) {
 				// We could have an incoming change or deletion
 				if (remote == null) {
+					String mode = KSubstOption.fromPattern(local.getName()).toMode();
 					info =  new ResourceSyncInfo(local.getName(), ResourceSyncInfo.ADDED_REVISION, ResourceSyncInfo.DUMMY_TIMESTAMP,
-								CVSProvider.isText(local.getName()) ? ResourceSyncInfo.USE_SERVER_MODE:ResourceSyncInfo.BINARY_TAG, local.getParent().getFolderSyncInfo().getTag(), null);
+						mode, local.getParent().getFolderSyncInfo().getTag(), null);
 					revision = info.getRevision();
 				} else {
 					info = remote.getSyncInfo();
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java
index b64af30..4148857 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFolder.java
@@ -254,9 +254,9 @@
 						monitor.beginTask(null, 100);
 						try {
 							EclipseSynchronizer.getInstance().beginOperation(Policy.subMonitorFor(monitor, 5));
-							job.run(Policy.subMonitorFor(monitor, 85));
+							job.run(Policy.subMonitorFor(monitor, 60));
 						} finally {
-							EclipseSynchronizer.getInstance().endOperation(Policy.subMonitorFor(monitor, 8));
+							EclipseSynchronizer.getInstance().endOperation(Policy.subMonitorFor(monitor, 35));
 						}
 					} catch(CVSException e) {
 						error[0] = e; 
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
index 133330b..02a758c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/ResourceSyncInfo.java
@@ -31,7 +31,6 @@
 	
 	// utility constants
 	private static final String DIRECTORY_PREFIX = "D/"; //$NON-NLS-1$
-	public static final String BINARY_TAG = "-kb"; //$NON-NLS-1$
 	public static final String USE_SERVER_MODE = ""; //$NON-NLS-1$
 	private static final String SEPERATOR = "/"; //$NON-NLS-1$
 	
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
index 4425534..7352733 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
@@ -198,7 +198,7 @@
 						if (resource.getType() == IResource.PROJECT) {
 							IProject project = (IProject)resource;
 							RepositoryProvider provider = RepositoryProviderType.getProvider(project);
-							if (! provider.isOfType(CVSProviderPlugin.getTypeId())) continue;
+							if (provider!=null && !provider.isOfType(CVSProviderPlugin.getTypeId())) continue;
 							// First, check if the .vcm_meta file for the project is in the delta.
 							IResourceDelta[] children = delta.getAffectedChildren(IResourceDelta.REMOVED | IResourceDelta.ADDED | IResourceDelta.CHANGED);
 							boolean inSync = false;
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaSyncHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaSyncHandler.java
index 80bc5e4..0058a9b 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaSyncHandler.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaSyncHandler.java
@@ -204,7 +204,7 @@
 								provider = RepositoryProviderType.getProvider(destination.getProject());
 							}
 							
-							if(provider.isOfType(CVSProviderPlugin.getTypeId())) {
+							if(provider!=null && provider.isOfType(CVSProviderPlugin.getTypeId())) {
 								delta.accept(visitor);
 							}
 						}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java
index e4d79e7..f354488 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java
@@ -56,7 +56,7 @@
 								provider = RepositoryProviderType.getProvider(destination.getProject());
 							}
 							
-							if(provider.isOfType(CVSProviderPlugin.getTypeId())) {
+							if(provider != null && provider.isOfType(CVSProviderPlugin.getTypeId())) {
 								delta.accept(visitor);
 							}
 						}
diff --git a/bundles/org.eclipse.team.cvs.ui/plugin.properties b/bundles/org.eclipse.team.cvs.ui/plugin.properties
index c5da82b..9b754a6 100644
--- a/bundles/org.eclipse.team.cvs.ui/plugin.properties
+++ b/bundles/org.eclipse.team.cvs.ui/plugin.properties
@@ -60,6 +60,9 @@
 TagAction.label=&Tag...
 TagAction.tooltip=Tag
 
+SetKeywordSubstitutionAction.label=&Set Keyword Substitution...
+SetKeywordSubstitutionAction.tooltip=Set Keyword Substitution for Files on CVS Server
+
 DefineBranchAction.label=&Define Branch Tag...
 DefineBranchAction.tooltip=Define a Branch Tag that Exists on CVS Server
 
diff --git a/bundles/org.eclipse.team.cvs.ui/plugin.xml b/bundles/org.eclipse.team.cvs.ui/plugin.xml
index afbae06..98428eb 100644
--- a/bundles/org.eclipse.team.cvs.ui/plugin.xml
+++ b/bundles/org.eclipse.team.cvs.ui/plugin.xml
@@ -58,6 +58,12 @@
 			objectClass="org.eclipse.core.resources.IResource" adaptable="true"> 
 			<filter name="projectNature" value="org.eclipse.team.cvs.core.cvsnature"/>
 			<action 
+				id="org.eclipse.team.ccvs.ui.setKeywordSubstitution"
+				label="%SetKeywordSubstitutionAction.label"
+				tooltip="%SetKeywordSubstitutionAction.tooltip"
+				menubarPath="team.main/group2"
+				class="org.eclipse.team.internal.ccvs.ui.actions.SetKeywordSubstitutionAction"/>
+			<action 
 				id="org.eclipse.team.ccvs.ui.tag"
 				label="%TagAction.label"
 				tooltip="%TagAction.tooltip"
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorationRunnable.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorationRunnable.java
index 06fb6d3..f704818 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorationRunnable.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorationRunnable.java
@@ -31,6 +31,7 @@
 import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
@@ -51,6 +52,11 @@
 	// Provides resources to be decorated and is notified when decoration has been calculated
 	private IDecorationNotifier notifier;
 
+	// Remember the non posted decorated resources
+	List resources = new ArrayList();
+	List decorations = new ArrayList();
+	private final static int NUM_TO_BATCH = 50;
+
 	/*
 	 * Define a cached image descriptor which only creates the image data once
 	 */
@@ -89,33 +95,48 @@
 			if (resource == null) {
 				return;
 			}
-			// it is possible that the resource to be decorated is no longer associated
-			// with a CVS provider. This could happen if the team nature was removed
-			// between the time the decoration event was posted to the thread and the time
-			// the thread processes the decoration.
-			RepositoryProvider provider = RepositoryProviderType.getProvider(resource.getProject());
-			if(!resource.exists() || provider==null || !provider.isOfType(CVSProviderPlugin.getTypeId())) {
-				continue;
-			}
 			
-			// determine a if resource has outgoing changes (e.g. is dirty).
-			IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
-			boolean isDirty = false;
-			boolean computeDeepDirtyCheck = store.getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY);
-			int type = resource.getType();
-			if(type == IResource.FILE || computeDeepDirtyCheck) {
-				isDirty = isDirty(resource);
-			}
-
-			// compute decorations						
-			CVSDecoration decoration = computeTextLabelFor(resource, isDirty);
-			decoration.setOverlays(computeLabelOverlaysFor(resource, isDirty, (CVSTeamProvider)provider));
+			CVSDecoration decoration = decorate(resource);
 			
 			// notify that decoration is ready
-			notifier.decorated(resource, decoration);
+			if(decoration!=null) {
+				resources.add(resource);
+				decorations.add(decoration);
+				if(!resources.isEmpty() && (notifier.remaining()==0 || resources.size() >= NUM_TO_BATCH)) {
+					notifier.decorated((IResource[])resources.toArray(new IResource[resources.size()]), 
+									   (CVSDecoration[])decorations.toArray(new CVSDecoration[decorations.size()]));
+					resources.clear();
+					decorations.clear();
+				}
+			}
 		}
 	}
 
+	public CVSDecoration decorate(IResource resource) {
+		// it is possible that the resource to be decorated is no longer associated
+		// with a CVS provider. This could happen if the team nature was removed
+		// between the time the decoration event was posted to the thread and the time
+		// the thread processes the decoration.
+		RepositoryProvider provider = RepositoryProviderType.getProvider(resource.getProject());
+		if(!resource.exists() || provider==null || !provider.isOfType(CVSProviderPlugin.getTypeId())) {
+			return null;
+		}
+			
+		// determine a if resource has outgoing changes (e.g. is dirty).
+		IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
+		boolean isDirty = false;
+		boolean computeDeepDirtyCheck = store.getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY);
+		int type = resource.getType();
+		if(type == IResource.FILE || computeDeepDirtyCheck) {
+			isDirty = isDirty(resource);
+		}
+
+		// compute decorations						
+		CVSDecoration decoration = computeTextLabelFor(resource, isDirty);
+		decoration.setOverlays(computeLabelOverlaysFor(resource, isDirty, (CVSTeamProvider)provider));
+		return decoration;
+	}
+	
 	private CVSDecoration computeTextLabelFor(IResource resource, boolean isDirty) {
 		Map bindings = new HashMap(3);
 		String format = ""; //$NON-NLS-1$
@@ -172,13 +193,17 @@
 						} else {
 							bindings.put(CVSDecoratorConfiguration.FILE_REVISION, fileInfo.getRevision());
 						}
-						bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, CVSDecorator.getFileTypeString(fileInfo.getName(), fileInfo.getKeywordMode()));
+						KSubstOption option = fileInfo.getKeywordMode() != null ?
+							KSubstOption.fromMode(fileInfo.getKeywordMode()) :
+							KSubstOption.fromPattern(fileInfo.getName());
+						bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, option.getShortDisplayText());
 						if (tag != null && (tag.getType() != CVSTag.HEAD)) {
 							bindings.put(CVSDecoratorConfiguration.RESOURCE_TAG, tag.getName());
 						}
 					} else {
 						// only show the type that cvs will use when comitting the file
-						bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, CVSDecorator.getFileTypeString(file.getName(), null));
+						KSubstOption option = KSubstOption.fromPattern(file.getName());
+						bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, option.getShortDisplayText());
 					}
 					break;
 			}			
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
index f96a0ea..afa2608 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
@@ -33,7 +33,8 @@
 import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.internal.ccvs.core.CVSProvider;
-import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
+import org.eclipse.team.internal.ccvs.core.client.Command;import org.eclipse.team.internal.ccvs.core.util.Assert;
 import org.eclipse.team.internal.ccvs.core.util.ResourceDeltaVisitor;
 
 /**
@@ -81,6 +82,7 @@
 		}
 		protected void finished() {
 			resourceStateChanged((IResource[])changedResources.toArray(new IResource[changedResources.size()]));
+			changedResources.clear();
 		}
 		protected int getEventMask() {
 			return IResourceChangeEvent.PRE_AUTO_BUILD;
@@ -88,12 +90,7 @@
 	}
 	
 	public CVSDecorator() {
-		// The decorator is a singleton, there should never be more than one instance.
-		// temporary until the UI component properly calls dispose when the workbench shutsdown
-		// UI Bug 9633
-		Assert.isTrue(theDecorator==null);
-		theDecorator = this;
-		
+		// thread that calculates the decoration for a resource
 		decoratorUpdateThread = new Thread(new CVSDecorationRunnable(this), "CVS"); //$NON-NLS-1$
 		decoratorUpdateThread.start();
 		CVSProviderPlugin.addResourceStateChangeListener(this);
@@ -191,18 +188,26 @@
 	/*
 	 * @see IDecorationNotifier#notify(IResource, CVSDecoration)
 	 */
-	public synchronized void decorated(IResource resource, CVSDecoration decoration) {
-		// ignore resources that aren't in the workbench anymore.
-		if(resource.exists() && !shutdown) {
-			cache.put(resource, decoration);
-			postLabelEvents(new LabelProviderChangedEvent[] { new LabelProviderChangedEvent(this, resource)});
+	public synchronized void decorated(IResource[] resources, CVSDecoration[] decorations) {
+		List events = new ArrayList();
+		if(!shutdown) {
+			for (int i = 0; i < resources.length; i++) {
+				IResource resource= resources[i];
+				if(resource.exists()) {
+					cache.put(resource, decorations[i]);
+					events.add(new LabelProviderChangedEvent(this, resource));
+				}
+			}
+			postLabelEvents((LabelProviderChangedEvent[]) events.toArray(new LabelProviderChangedEvent[events.size()]));
 		}
 	}
 
 	/*
 	 * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
 	 */
-
+	public int remaining() {
+		return decoratorNeedsUpdating.size();
+	}
 	/*
 	 * @see IResourceStateChangeListener#resourceStateChanged(IResource[])
 	 */
@@ -345,33 +350,9 @@
 		}
 	}
 
-	public static void shutdownAll() {
-		if(theDecorator!=null) {
-			theDecorator.dispose();
-		}
-	}
+
 	
-	public static String getFileTypeString(String name, String keyword) {
-		StringBuffer buffer = new StringBuffer();
-		boolean isBinary = false;
-		if(keyword!=null) {
-			if (keyword.equals("-kb")) { //$NON-NLS-1$
-				isBinary = true;
-			}
-		} else {
-			isBinary = !CVSProvider.isText(name);
-		}
-		
-		if(isBinary) {
-			buffer.append(Policy.bind("CVSFilePropertiesPage.binary")); 
-		} else {
-			buffer.append(Policy.bind("CVSFilePropertiesPage.text"));
-			if(keyword!=null && !keyword.equals("-ko") && !"".equals(keyword)) { //$NON-NLS-1$ //$NON-NLS-2$
-				buffer.append(" " + keyword); //$NON-NLS-1$
-			}
-		}		
-		return buffer.toString();
-	}
+
 	
 	/*
 	 * @see IBaseLabelProvider#dispose()
@@ -383,7 +364,7 @@
 		shutdown();
 		
 		// unregister change listeners
-		changeListener.register();
+		changeListener.deregister();
 		CVSProviderPlugin.removeResourceStateChangeListener(this);
 		
 		// dispose of images created as overlays
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
index 4bfe11a..51da566 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
@@ -32,6 +32,7 @@
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPreferencePage;
@@ -104,7 +105,8 @@
 		try {
 			ICVSRepositoryLocation location = CVSRepositoryLocation.fromString(":pserver:username@host.acme.org:/home/cvsroot");				 //$NON-NLS-1$
 			bindings.put(CVSDecoratorConfiguration.RESOURCE_TAG, "v2_0"); //$NON-NLS-1$
-			bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, CVSDecorator.getFileTypeString("file.txt",null)); //$NON-NLS-1$
+			bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD,
+				KSubstOption.fromPattern("file.txt").getShortDisplayText()); //$NON-NLS-1$
 			bindings.put(CVSDecoratorConfiguration.FILE_REVISION, "1.34"); //$NON-NLS-1$
 			bindings.put(CVSDecoratorConfiguration.DIRTY_FLAG, dirtyFlag.getText());
 			bindings.put(CVSDecoratorConfiguration.ADDED_FLAG, addedFlag.getText());
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSFilePropertiesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSFilePropertiesPage.java
index 300dc96..66018f2 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSFilePropertiesPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSFilePropertiesPage.java
@@ -16,6 +16,7 @@
 import org.eclipse.team.ccvs.core.CVSTag;
 import org.eclipse.team.ccvs.core.ICVSFile;
 import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 import org.eclipse.ui.dialogs.PropertyPage;
@@ -62,16 +63,7 @@
 			
 			// Keyword Mode
 			createLabel(composite, Policy.bind("CVSFilePropertiesPage.keywordMode"));
-			String keywordMode = syncInfo.getKeywordMode();
-			if (keywordMode.equals("-kb")) {
-				createLabel(composite, Policy.bind("CVSFilePropertiesPage.binary")); 
-			} else if (keywordMode.equals("-ko")) {
-				createLabel(composite, Policy.bind("CVSFilePropertiesPage.textNoKeyword"));
-			} else if (keywordMode.equals("")) {
-				createLabel(composite, Policy.bind("CVSFilePropertiesPage.text"));
-			} else {
-				createLabel(composite, keywordMode);
-			}
+			createLabel(composite, KSubstOption.fromMode(syncInfo.getKeywordMode()).getLongDisplayText());
 			
 			// Tag
 			createLabel(composite, Policy.bind("CVSFilePropertiesPage.tag"));
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java
index 8cfc623..2832e08 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java
@@ -17,9 +17,11 @@
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.WizardDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
@@ -28,13 +30,16 @@
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.ccvs.core.CVSTag;
 import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.ICVSFolder;
 import org.eclipse.team.ccvs.core.ICVSRemoteResource;
 import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
 import org.eclipse.team.ccvs.core.IUserInfo;
 import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.ui.wizards.UpdateWizard;
 import org.eclipse.ui.dialogs.PropertyPage;
 
 public class CVSPropertiesPage extends PropertyPage {
@@ -48,8 +53,10 @@
 	Label pathLabel;
 	Label moduleLabel;
 	Label portLabel;
+	Label tagLabel;
 	
 	boolean passwordChanged;
+	boolean connectionInfoChanged;
 
 	IUserInfo info;
 	CVSTeamProvider provider;
@@ -63,35 +70,65 @@
 		Composite composite = new Composite(parent, SWT.NULL);
 		composite.setLayoutData(new GridData(GridData.FILL_BOTH));
 		GridLayout layout = new GridLayout();
-		layout.numColumns = 2;
+		layout.numColumns = 3;
 		composite.setLayout(layout);
 		
-		Label label = createLabel(composite, Policy.bind("CVSPropertiesPage.connectionType"));
+		Label label = createLabel(composite, Policy.bind("CVSPropertiesPage.connectionType"), 1);
 		methodType = createCombo(composite);
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.user"));
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.user"), 1);
 		userText = createTextField(composite);
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.password"));
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.password"), 1);
 		passwordText = createTextField(composite);
 		passwordText.setEchoChar('*');
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.host"));
-		hostLabel = createLabel(composite, "");
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.host"), 1);
+		hostLabel = createLabel(composite, "", 2);
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.port"));
-		portLabel = createLabel(composite, "");
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.port"), 1);
+		portLabel = createLabel(composite, "", 2);
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.path"));
-		pathLabel = createLabel(composite, "");
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.path"), 1);
+		pathLabel = createLabel(composite, "", 2);
 		
-		label = createLabel(composite, Policy.bind("CVSPropertiesPage.module"));
-		moduleLabel = createLabel(composite, "");
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.module"), 1);
+		moduleLabel = createLabel(composite, "", 2);
+		
+		label = createLabel(composite, Policy.bind("CVSPropertiesPage.tag"), 1);
+		tagLabel = createLabel(composite, "", 1);
+		GridData data = new GridData(GridData.FILL_HORIZONTAL);
+		tagLabel.setLayoutData(data);
+		
+		Button changeTag = new Button(composite, SWT.PUSH);
+		data = new GridData(GridData.HORIZONTAL_ALIGN_END);
+		changeTag.setLayoutData(data);
+		changeTag.setText(Policy.bind("CVSPropertiesPage.update"));
+		changeTag.addListener(SWT.Selection, new Listener() {
+			public void handleEvent(Event event) {
+				UpdateWizard wizard = new UpdateWizard();
+				wizard.setProject(project);
+				WizardDialog dialog = new WizardDialog(getShell(), wizard);
+				dialog.open();
+				initializeTag();
+			}
+		});
 		
 		initializeValues();
 		passwordText.addListener(SWT.Modify, new Listener() {
 			public void handleEvent(Event event) {
 				passwordChanged = true;
+				connectionInfoChanged = true;
+			}
+		});
+		userText.addListener(SWT.Modify, new Listener() {
+			public void handleEvent(Event event) {
+				connectionInfoChanged = true;
+			}
+		});
+		methodType.addListener(SWT.Modify, new Listener() {
+			public void handleEvent(Event event) {
+				connectionInfoChanged = true;
 			}
 		});
 		return composite;
@@ -106,6 +143,7 @@
 		Combo combo = new Combo(parent, SWT.READ_ONLY);
 		GridData data = new GridData(GridData.FILL_HORIZONTAL);
 		data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+		data.horizontalSpan = 2;
 		combo.setLayoutData(data);
 		return combo;
 	}
@@ -117,11 +155,11 @@
 	 * @param text  the text for the new label
 	 * @return the new label
 	 */
-	protected Label createLabel(Composite parent, String text) {
+	protected Label createLabel(Composite parent, String text, int span) {
 		Label label = new Label(parent, SWT.LEFT);
 		label.setText(text);
 		GridData data = new GridData();
-		data.horizontalSpan = 1;
+		data.horizontalSpan = span;
 		data.horizontalAlignment = GridData.FILL;
 		label.setLayoutData(data);
 		return label;
@@ -138,6 +176,7 @@
 		data.verticalAlignment = GridData.CENTER;
 		data.grabExcessVerticalSpace = false;
 		data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+		data.horizontalSpan = 2;
 		text.setLayoutData(data);
 		return text;
 	}
@@ -163,7 +202,6 @@
 	private void initializeValues() {
 		passwordChanged = false;
 		
-		// property page is added with a nature filter - hence the unchecked typecast
 		provider = (CVSTeamProvider)RepositoryProviderType.getProvider(project);
 		if (provider == null) return;
 		
@@ -199,11 +237,35 @@
 		} catch (TeamException e) {
 			handle(e);
 		}
+		
+		initializeTag();
+	}
+	
+	private void initializeTag() {
+		provider = (CVSTeamProvider)RepositoryProviderType.getProvider(project);
+		if (provider == null) return;
+		CVSWorkspaceRoot cvsRoot = provider.getCVSWorkspaceRoot();
+		try {
+			ICVSFolder local = cvsRoot.getCVSFolderFor(project);
+			CVSTag tag = local.getFolderSyncInfo().getTag();
+			String tagName;
+			if (tag == null) {
+				tagName = "HEAD";
+			} else {
+				tagName = tag.getName();
+			}
+			tagLabel.setText(tagName);
+		} catch (TeamException e) {
+			handle(e);
+		}
 	}
 	/*
 	 * @see PreferencesPage#performOk
 	 */
 	public boolean performOk() {
+		if ( ! connectionInfoChanged) {
+			return true;
+		}
 		info.setUsername(userText.getText());
 		if (passwordChanged) {
 			info.setPassword(passwordText.getText());
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
index 6ecfad7..a6dd3d4 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
@@ -246,7 +246,6 @@
 	 * @see Plugin#shutdown()
 	 */
 	public void shutdown() throws CoreException {
-		CVSDecorator.shutdownAll();
 		changeListener.deregister();
 		super.shutdown();
 		try {
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IDecorationNotifier.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IDecorationNotifier.java
index 541086b..d465f58 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IDecorationNotifier.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IDecorationNotifier.java
@@ -17,5 +17,10 @@
 	/**
 	 * Called to associate a decoration to a resource.
 	 */
-	public void decorated(IResource resource, CVSDecoration decoration);
+	public void decorated(IResource[] resource, CVSDecoration[] decoration);
+	
+	/**
+	 * Number of resources remaining to be decorated
+	 */
+	public int remaining();
 }
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAsAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAsAction.java
index e620868..ddf36d5 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAsAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAsAction.java
@@ -10,10 +10,13 @@
 import java.util.Iterator;
 
 import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.IInputValidator;
 import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.viewers.IStructuredSelection;
@@ -78,7 +81,17 @@
 					String name = folders[0].getName();
 					// Prompt for name
 					final int[] result = new int[] { InputDialog.OK };
-					final InputDialog dialog = new InputDialog(shell, Policy.bind("CheckoutAsAction.enterProjectTitle"), Policy.bind("CheckoutAsAction.enterProject"), name, null);
+					final InputDialog dialog = new InputDialog(shell, Policy.bind("CheckoutAsAction.enterProjectTitle"), Policy.bind("CheckoutAsAction.enterProject"), name, 
+						new IInputValidator() {
+							public String isValid(String newText) {
+								IStatus status = ResourcesPlugin.getWorkspace().validateName(newText, IResource.PROJECT);
+								if (status.isOK()) {
+									return ""; //$NON-NLS-1$
+								}
+								return status.getMessage();
+							}
+	
+						});
 					shell.getDisplay().syncExec(new Runnable() {
 						public void run() {
 							result[0] = dialog.open();
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SetKeywordSubstitutionAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SetKeywordSubstitutionAction.java
index f783109..0febdc0 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SetKeywordSubstitutionAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/SetKeywordSubstitutionAction.java
@@ -8,10 +8,11 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
 import org.eclipse.team.ccvs.core.CVSTeamProvider;
 import org.eclipse.team.ccvs.core.ICVSResource;
-import org.eclipse.team.core.ITeamManager;
-import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.TeamPlugin;
 import org.eclipse.team.internal.ccvs.core.client.Command;
@@ -50,15 +51,14 @@
 	protected boolean isEnabled() throws TeamException {
 		IResource[] resources = getSelectedResources();
 		if (resources.length == 0) return false;
-		ITeamManager manager = TeamPlugin.getManager();
 		for (int i = 0; i < resources.length; i++) {
 			IResource resource = resources[i];
 			// resource must be local
 			if (! resource.isAccessible()) return false;
 			// provider must be CVS
-			ITeamProvider provider = manager.getProvider(resource.getProject());
+			RepositoryProvider provider = RepositoryProviderType.getProvider(resource.getProject());
 			if (provider == null) return false;
-			if (! (provider instanceof CVSTeamProvider)) return false;
+			if (!provider.isOfType(CVSProviderPlugin.getTypeId())) return false;
 			// resource must either be a project, or it must be managed
 			if (resource.getType() != IResource.PROJECT) {
 				ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties
index 8e2c587..dae2be4 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties
@@ -91,9 +91,6 @@
 CVSFilePropertiesPage.dirty=Dirty:
 CVSFilePropertiesPage.modified=Modified:
 CVSFilePropertiesPage.keywordMode=Keyword Mode:
-CVSFilePropertiesPage.binary=Binary
-CVSFilePropertiesPage.textNoKeyword=Text with no keyword expansion
-CVSFilePropertiesPage.text=Text
 CVSFilePropertiesPage.tag=Tag:
 CVSFilePropertiesPage.none=(none)
 CVSFilePropertiesPage.version={0} (Version)
@@ -113,6 +110,8 @@
 CVSPropertiesPage.path=Repository path:
 CVSPropertiesPage.module=Module:
 CVSPropertiesPage.defaultPort=Default
+CVSPropertiesPage.tag=Tag:
+CVSPropertiesPage.update=Change &Tag...
 
 CVSPreferencePage.pruneEmptyDirectories=&Prune empty directories
 CVSPreferencePage.timeoutValue=&Communication timeout value (in seconds)
@@ -321,6 +320,54 @@
 #ResourcePropertiesPage.none=none
 ResourcePropertiesPage.error=Error
 
+SetKeywordSubstitution.title=Set Keyword Substitution
+
+SetKeywordSubstitution.working=Setting keyword substitution mode...
+SetKeywordSubstitution.problemsMessage=Problems encountered setting keyword substitution
+SetKeywordSubstitution.errorTitle=Errors Occurred
+SetKeywordSubstitution.warningTitle=Warnings Occured
+
+SetKeywordSubstitution.SharedFilesPage.pageTitle=\
+  Warning some of the selected files are already shared in the repository.
+SetKeywordSubstitution.SharedFilesPage.pageDescription=\
+  Do you want to exclude these files from this operation?
+SetKeywordSubstitution.SharedFilesPage.contents=\
+  Since the keyword substitution mode attribute is not version controlled, the \
+  change will be reflected immediately on all branches and in all revisions of \
+  any affected files that are already shared in the repository.  You should inform \
+  all developers that they must delete and check out fresh copies of the affected \
+  files before committing new changes to the repository.  You may also experience \
+  side-effects working with earlier revisions of these files.
+SetKeywordSubstitution.SharedFilesPage.onlyAddedFilesButton=Don't change files that are already shared in the repository.
+SetKeywordSubstitution.SharedFilesPage.committedFiles=Selected files that are already shared in the repository:
+
+SetKeywordSubstitution.OutgoingChangesPage.pageTitle=\
+  Warning some of the selected files have outgoing changes.
+SetKeywordSubstitution.OutgoingChangesPage.pageDescription=\
+  Do you want to exclude these files from this operation?
+SetKeywordSubstitution.OutgoingChangesPage.contents=\
+  This operation may commit changes to files without providing a chance to resolve \
+  conflicts first.
+SetKeywordSubstitution.OutgoingChangesPage.onlyCleanFilesButton=Don't change files with outgoing changes.
+SetKeywordSubstitution.OutgoingChangesPage.changedFiles=Selected files with outgoing changes.
+
+SetKeywordSubstitution.SelectModePage.pageTitle=Select and change keyword substitution mode
+SetKeywordSubstitution.SelectModePage.pageDescription=\
+  The keyword substitution mode specifies transformations to be performed \
+  on files during CVS operations.
+SetKeywordSubstitution.SelectModePage.contents=\
+  If you are uncertain, choose 'Text' for plain text files, and 'Binary' for all others.
+SetKeywordSubstitution.SelectModePage.binaryLabel=\
+  Binary files are stored in the repository without alteration.\n\
+  CVS does not directly support conflict resolution for binary files.
+SetKeywordSubstitution.SelectModePage.textLabel=\
+  Text files are stored in the repository in a neutral format but are retrieved in the platform's native text encoding.\n\
+  CVS supports conflict resoltion and differencing of text files with single line granularity.
+SetKeywordSubstitution.SelectModePage.textWithSubstitutions=Text with Keyword Substitution...
+SetKeywordSubstitution.SelectModePage.textWithSubstitutionsLabel=\
+  Text files may also contain keywords that are substituted with their value by the CVS server during file transfers.\n\
+  Please consult the CVS documentation for a description of the various supported keywords and modes.
+
 SharingWizard.autoConnectTitle=Connect Project to Repository
 SharingWizard.couldNotImport=Could Not Import
 SharingWizard.couldNotImportLong=Your resources could not be imported into the repository because a module with that name already exists.
@@ -351,6 +398,12 @@
 UpdateAction.update=Problems encountered performing update
 UpdateAction.updating=Updating...
 
+UpdateWizard.title=Update
+UpdateWizard.updatePage=Update
+
+UpdateWizardPage.description=&Select a tag to update the project sharing to:
+UpdateWizardPage.overwrite=&Overwrite local changes
+
 UserValidationDialog.required=Password Required
 UserValidationDialog.labelUser=Enter a user name and password for the following repository:\n{0}
 UserValidationDialog.labelPassword=Enter a password for {0} in the following repository:\n{1}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
index b204b7b..01be6ee 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
@@ -30,9 +30,9 @@
 import org.eclipse.team.core.sync.IRemoteResource;
 import org.eclipse.team.core.sync.IRemoteSyncElement;
 import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
-import org.eclipse.team.internal.ccvs.ui.CVSDecorator;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.HistoryView;
 import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
@@ -172,13 +172,10 @@
 						try {
 							ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor((IFile) resource);
 							ResourceSyncInfo info = cvsFile.getSyncInfo();
-							String kw;
-							if (info!=null) {
-								kw = CVSDecorator.getFileTypeString(resource.getName(), info.getKeywordMode());
-							} else {
-								kw = CVSDecorator.getFileTypeString(resource.getName(), null);
-							}
-							postfix.append("(" + kw + ")");
+							KSubstOption option = info != null && info.getKeywordMode() != null ?
+								KSubstOption.fromMode(info.getKeywordMode()) :
+								KSubstOption.fromPattern(resource.getName());
+							postfix.append("(" + option.getShortDisplayText() + ")");
 						} catch(CVSException e) {
 							ErrorDialog.openError(getControl().getShell(), null, null, e.getStatus());
 						}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizard.java
index b696818..9aa07a9 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizard.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizard.java
@@ -20,10 +20,6 @@
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.wizard.Wizard;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
 import org.eclipse.team.ccvs.core.CVSTag;
 import org.eclipse.team.ccvs.core.CVSTeamProvider;
 import org.eclipse.team.ccvs.core.ICVSRemoteFolder;
@@ -42,7 +38,7 @@
 public class BranchWizard extends Wizard {
 	BranchWizardVersionPage versionPage;
 	BranchWizardBranchPage branchPage;
-	BranchWizardMethodPage methodPage;
+	//BranchWizardMethodPage methodPage;
 	IResource[] resources;
 	
 	public BranchWizard() {
@@ -55,8 +51,8 @@
 		addPage(versionPage);
 		branchPage = new BranchWizardBranchPage("branchPage", Policy.bind("BranchWizard.createABranch"), CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_WIZBAN_BRANCH));
 		addPage(branchPage);
-		methodPage = new BranchWizardMethodPage("methodPage", Policy.bind("BranchWizard.createABranch"), CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_WIZBAN_BRANCH));
-		addPage(methodPage);
+		//methodPage = new BranchWizardMethodPage("methodPage", Policy.bind("BranchWizard.createABranch"), CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_WIZBAN_BRANCH));
+		//addPage(methodPage);
 	}
 	public boolean performFinish() {
 		final boolean[] result = new boolean[] {false};
@@ -71,7 +67,8 @@
 						if (versionString != null) {
 							versionTag = new CVSTag(versionString, CVSTag.VERSION);
 						}
-						boolean eclipseWay = methodPage.getEclipseWay();
+						boolean eclipseWay = true;
+						//boolean eclipseWay = methodPage.getEclipseWay();
 						
 						// To do: use the wizard's progress monitor
 						RepositoryManager manager = CVSUIPlugin.getPlugin().getRepositoryManager();
@@ -146,25 +143,4 @@
 		}
 		return result;
 	}
-	
-	/**
-	 * A helper method used by pages to display indented descriptions
-	 */
-	protected static Composite createDescriptionComposite(Composite parent, int parentColumns) {
-		Composite composite = new Composite(parent, SWT.NULL);
-	
-		// GridLayout
-		GridLayout layout = new GridLayout();
-		layout.marginWidth = 10;
-		layout.marginHeight = 0;
-		composite.setLayout(layout);
-	
-		// GridData
-		GridData data = new GridData();
-		data.verticalAlignment = GridData.FILL;
-		data.horizontalAlignment = GridData.FILL;
-		data.horizontalSpan = parentColumns;
-		composite.setLayoutData(data);
-		return composite;
-	}
 }
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizardMethodPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizardMethodPage.java
index bb05774..c74045b 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizardMethodPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/BranchWizardMethodPage.java
@@ -53,7 +53,7 @@
 		data.horizontalSpan = 2;
 		eclipseWay.setLayoutData(data);
 		
-		Composite eclipseDescriptionComposite = BranchWizard.createDescriptionComposite(composite, 2);
+		Composite eclipseDescriptionComposite = createDescriptionComposite(composite, 2);
 		Label eclipseDescription = new Label(eclipseDescriptionComposite, SWT.WRAP);
 		eclipseDescription.setText(Policy.bind("BranchWizardMethodPage.eclipseDescription")); //$NON-NLS-1$
 		data = new GridData();
@@ -72,7 +72,7 @@
 		data.horizontalSpan = 2;
 		cvsWay.setLayoutData(data);
 		
-		Composite cvsDescriptionComposite = BranchWizard.createDescriptionComposite(composite, 2);
+		Composite cvsDescriptionComposite = createDescriptionComposite(composite, 2);
 		Label cvsDescription = new Label(cvsDescriptionComposite, SWT.WRAP);
 		cvsDescription.setText(Policy.bind("BranchWizardMethodPage.cvsDescription")); //$NON-NLS-1$
 		data = new GridData();
@@ -90,4 +90,22 @@
 	private void updateEnablement() {
 		setPageComplete(true);
 	}
+	
+	private Composite createDescriptionComposite(Composite parent, int parentColumns) {
+		Composite composite = new Composite(parent, SWT.NULL);
+	
+		// GridLayout
+		GridLayout layout = new GridLayout();
+		layout.marginWidth = 10;
+		layout.marginHeight = 0;
+		composite.setLayout(layout);
+	
+		// GridData
+		GridData data = new GridData();
+		data.verticalAlignment = GridData.FILL;
+		data.horizontalAlignment = GridData.FILL;
+		data.horizontalSpan = parentColumns;
+		composite.setLayoutData(data);
+		return composite;
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SetKeywordSubstitutionOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SetKeywordSubstitutionOperation.java
index 3a66af8..f36ca43 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SetKeywordSubstitutionOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SetKeywordSubstitutionOperation.java
@@ -22,9 +22,9 @@
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.team.ccvs.core.CVSStatus;
 import org.eclipse.team.ccvs.core.CVSTeamProvider;
-import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.core.TeamException;
-import org.eclipse.team.core.TeamPlugin;
 import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.Policy;
@@ -115,7 +115,7 @@
 	protected Hashtable getProviderMapping(IResource[] resources) {
 		Hashtable result = new Hashtable();
 		for (int i = 0; i < resources.length; i++) {
-			ITeamProvider provider = TeamPlugin.getManager().getProvider(resources[i].getProject());
+			RepositoryProvider provider = RepositoryProviderType.getProvider(resources[i].getProject());
 			List list = (List)result.get(provider);
 			if (list == null) {
 				list = new ArrayList();
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/UpdateWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/UpdateWizard.java
index c5cbd6e..0e23663 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/UpdateWizard.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/UpdateWizard.java
@@ -17,6 +17,7 @@
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.wizard.Wizard;
 import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.RepositoryProviderType;
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.TeamPlugin;
 import org.eclipse.team.internal.ccvs.core.CVSException;
@@ -62,7 +63,7 @@
 			getContainer().run(false, false, new IRunnableWithProgress() {
 				public void run(IProgressMonitor monitor) throws InvocationTargetException {
 					try {
-						CVSTeamProvider provider = ((CVSTeamProvider)TeamPlugin.getManager().getProvider(project));	
+						CVSTeamProvider provider = ((CVSTeamProvider)RepositoryProviderType.getProvider(project));	
 						provider.update(new IResource[] {project}, updatePage.getLocalOptions(), updatePage.getTag(), null, monitor);
 						result[0] = true;
 					} catch (TeamException e) {
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
index 6db1032..84be67f 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/CVSTestSetup.java
@@ -36,7 +36,7 @@
 	}
 
 	public static void loadProperties() {
-		String propertiesFile = System.getProperty("eclipse.cvs.properties");
+		String propertiesFile = "e:/repository.properties";
 		if (propertiesFile == null) return;
 		File file = new File(propertiesFile);
 		if (file.isDirectory()) file = new File(file, "repository.properties");
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/CVSProviderTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/CVSProviderTest.java
index c575e91..c4a4dc5 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/CVSProviderTest.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/CVSProviderTest.java
@@ -3,18 +3,33 @@
  * (c) Copyright IBM Corp. 2000, 2002.
  * All Rights Reserved.
  */
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
+import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.team.ccvs.core.CVSProviderPlugin;
 import org.eclipse.team.ccvs.core.CVSTag;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.ICVSFile;
+import org.eclipse.team.core.IFileTypeRegistry;
 import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
 import org.eclipse.team.internal.ccvs.core.client.Command;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 import org.eclipse.team.tests.ccvs.core.CVSTestSetup;
 import org.eclipse.team.tests.ccvs.core.EclipseTest;
 import org.eclipse.team.tests.ccvs.core.JUnitTestCase;
@@ -218,5 +233,139 @@
 		getProvider(copy).get(new IResource[] {copy}, IResource.DEPTH_INFINITE, DEFAULT_MONITOR);
 		assertEquals(project, copy);
 	}
+	
+	public void testCleanLineDelimiters() throws TeamException, CoreException, IOException {
+		// Create a project
+		IProject project = getUniqueTestProject("testCleanLineDelimiters");
+		IFile file = project.getFile("testfile");
+		IProgressMonitor monitor = new NullProgressMonitor();
+
+		// empty file
+		setFileContents(file, "");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "");
+		
+		// one byte
+		setFileContents(file, "a");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "a");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "a");
+		
+		// single orphan carriage return (should be preserved)
+		setFileContents(file, "\r");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "\r");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "\r");
+
+		// single line feed
+		setFileContents(file, "\n");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "\n");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "\r\n");
+		
+		// single carriage return line feed
+		setFileContents(file, "\r\n");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "\r\n");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "\n");
+		
+		// mixed text with orphaned CR's
+		setFileContents(file, "The \r\n quick brown \n fox \r\r\r\n jumped \n\n over \r\n the \n lazy dog.\r\n");
+		CVSTeamProvider.cleanLineDelimiters(file, false, monitor);
+		assertEqualsFileContents(file, "The \n quick brown \n fox \r\r\n jumped \n\n over \n the \n lazy dog.\n");
+		setFileContents(file, "The \r\n quick brown \n fox \r\r\r\n jumped \n\n over \r\n the \n lazy dog.\r\n");
+		CVSTeamProvider.cleanLineDelimiters(file, true, monitor);
+		assertEqualsFileContents(file, "The \r\n quick brown \r\n fox \r\r\r\n jumped \r\n\r\n over \r\n the \r\n lazy dog.\r\n");
+	}
+	
+	public void testKeywordSubstitution() throws TeamException, CoreException, IOException {
+		testKeywordSubstitution(Command.KSUBST_BINARY); // -kb
+		testKeywordSubstitution(Command.KSUBST_TEXT); // -ko
+		testKeywordSubstitution(Command.KSUBST_TEXT_EXPAND); // -kkv
+	}
+
+	private void testKeywordSubstitution(KSubstOption ksubst) throws TeamException, CoreException, IOException {
+		// setup some known file types
+		TeamPlugin.getFileTypeRegistry().setValue("xbin", IFileTypeRegistry.BINARY);
+		TeamPlugin.getFileTypeRegistry().setValue("xtxt", IFileTypeRegistry.TEXT);
+		
+		// create a test project
+		IProject project = createProject("testKeywordSubstitution", new String[] { "dummy" });
+		addResources(project, new String[] { "binary.xbin", "text.xtxt", "folder1/", "folder1/a.xtxt" }, true);
+		addResources(project, new String[] { "added.xbin", "added.xtxt" }, false);
+		assertHasKSubstOption(project, "binary.xbin", Command.KSUBST_BINARY);
+		assertHasKSubstOption(project, "added.xbin", Command.KSUBST_BINARY);
+		// XXX should these be KSUBST_TEXT instead?
+		assertHasKSubstOption(project, "text.xtxt", Command.KSUBST_TEXT_EXPAND);
+		assertHasKSubstOption(project, "folder1/a.xtxt", Command.KSUBST_TEXT_EXPAND);
+		assertHasKSubstOption(project, "added.xtxt", Command.KSUBST_TEXT_EXPAND);
+		
+		// change keyword substitution
+		IStatus status = getProvider(project).setKeywordSubstitution(new IResource[] { project },
+			IResource.DEPTH_INFINITE, ksubst, null);
+		assertTrue("Status should be ok, was: " + status.toString(), status.isOK());
+		assertHasKSubstOption(project, "binary.xbin", ksubst);
+		assertHasKSubstOption(project, "text.xtxt", ksubst);
+		assertHasKSubstOption(project, "folder1/a.xtxt", ksubst);
+		assertHasKSubstOption(project, "added.xtxt", ksubst);
+		assertHasKSubstOption(project, "added.xbin", ksubst);
+
+		// verify that substitution mode changed remotely and "added.xtxt", "added.xbin" don't exist
+		IProject copy = checkoutCopy(project, "-copy");
+		assertHasKSubstOption(copy, "binary.xbin", ksubst);
+		assertHasKSubstOption(copy, "text.xtxt", ksubst);
+		assertHasKSubstOption(copy, "folder1/a.xtxt", ksubst);
+		assertDoesNotExistInWorkspace(copy.getFile("added.xtxt"));
+		assertDoesNotExistInWorkspace(copy.getFile("added.xbin"));
+		
+		// commit added files then checkout the copy again
+		commitResources(project, new String[] { "added.xbin", "added.xtxt" });
+		IProject copy2 = checkoutCopy(project, "-copy2");
+		assertHasKSubstOption(copy2, "added.xtxt", ksubst);
+		assertHasKSubstOption(copy2, "added.xbin", ksubst);
+		
+		// verify that local contents are up to date
+		assertEquals(project, copy2);
+	}
+	
+	public static void setFileContents(IFile file, String string) throws CoreException {
+		InputStream is = new ByteArrayInputStream(string.getBytes());
+		if (file.exists()) {
+			file.setContents(is, false /*force*/, true /*keepHistory*/, null);
+		} else {
+			file.create(is, false /*force*/, null);
+		}
+	}
+	public static String getFileContents(IFile file) throws CoreException, IOException {
+		StringBuffer buf = new StringBuffer();
+		InputStream is = new BufferedInputStream(file.getContents());
+		try {
+			int c;
+			while ((c = is.read()) != -1) buf.append((char)c);
+		} finally {
+			is.close();
+		}
+		return buf.toString();
+	}
+	
+	public static void assertEqualsFileContents(IFile file, String string) throws CoreException, IOException {
+		String other = getFileContents(file);
+		assertEquals(string, other);
+	}
+	
+	public static void assertHasKSubstOption(IContainer container, String filename, KSubstOption ksubst)
+		throws TeamException {
+		IFile file = container.getFile(new Path(filename));
+		ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file);
+		ResourceSyncInfo info = cvsFile.getSyncInfo();
+		KSubstOption other = KSubstOption.fromMode(info.getKeywordMode());
+		assertEquals(ksubst, other);
+	}
 }
 
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/RemoteResourceTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/RemoteResourceTest.java
index 9006b82..f7c6c17 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/RemoteResourceTest.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/provider/RemoteResourceTest.java
@@ -230,7 +230,7 @@
 	 public void testFileRevisions() throws TeamException, CoreException, IOException {
 	 	
 	 	// Create a project with an empty file
-		IProject project = createProject("testEmptyFile", new String[] { "file.txt"});
+		IProject project = createProject("testFileRevisions", new String[] { "file.txt"});
 		IFile file = project.getFile("file.txt");
 		JUnitTestCase.waitMsec(1500);
 		file.setContents(new ByteArrayInputStream("hi there".getBytes()), false, false, DEFAULT_MONITOR);