Storing initial locking work on a branch so it is not lost
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java
new file mode 100644
index 0000000..d4e6777
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ReadOnlySchedulingRule.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The purpose of ReadOnlySchedulingRule is to prevent Write*SchedulingRule threads 
+ * from running concurrently with the read only thread. It does not prevent the read only 
+ * thread from modifying resources or sync info. Also, it does not prevent other threads from
+ * modifying resources. It is the reponsibility of clients to 
+ * choose the proper scheduling rule up front given the above mentioned characteristics.
+ */
+public class ReadOnlySchedulingRule extends RepositoryProviderSchedulingRule {
+	public ReadOnlySchedulingRule(RepositoryProvider provider) {
+		super(provider);
+	}
+	public boolean contains(ISchedulingRule rule) {
+		if (rule instanceof WriteSyncSchedulingRule) {
+			// Read rules cannot contain write rules
+			RepositoryProvider otherProvider = ((RepositoryProviderSchedulingRule)rule).getProvider();
+			if (otherProvider == getProvider()) return false;
+		}
+		return super.contains(rule);
+	}
+	public boolean isConflicting(ISchedulingRule rule) {
+		if (rule instanceof ReadOnlySchedulingRule) {
+			// Read rules never conflict with other reads
+			return false;
+		}
+		return super.isConflicting(rule);
+	}
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java
new file mode 100644
index 0000000..b855947
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/RepositoryProviderSchedulingRule.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * This class provides a scheduling rule that can be used to ensure exclusive access
+ * to the resources of a project under the control of a repository provider. When a
+ * RepositoryProviderSchedulingRule is held, the holding thread may still modify resources
+ * within the project. However, resources in other projects are not contained by a
+ * RepositoryProviderSchedulingRule.
+ */
+public class RepositoryProviderSchedulingRule implements ISchedulingRule {
+	
+	private RepositoryProvider provider;
+	
+	public RepositoryProviderSchedulingRule(RepositoryProvider provider) {
+		this.provider = provider;
+	}
+	
+	public RepositoryProvider getProvider() {
+		return provider;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
+	 */
+	public boolean contains(ISchedulingRule rule) {
+		if (rule instanceof RepositoryProviderSchedulingRule) {
+			// In general, RepositoryProviderSchedulingRule can contain other RepositoryProviderSchedulingRules
+			RepositoryProvider otherProvider = ((RepositoryProviderSchedulingRule)rule).getProvider();
+			if (otherProvider == getProvider()) return true;
+		} else if (rule instanceof IResource) {
+			// Rules can contain resource modifications in the provider's project
+			return getProvider().getProject().contains(rule);
+		}
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+	 */
+	public boolean isConflicting(ISchedulingRule rule) {
+		if (rule instanceof RepositoryProviderSchedulingRule) {
+			// In general, RepositoryProviderSchedulingRules on the same provider conflict
+			if (((RepositoryProviderSchedulingRule)rule).getProvider() == getProvider()) {
+				// We can use identity for the check since a RepositoryProvider
+				// is only created once for each project
+				return true;
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java
new file mode 100644
index 0000000..72d07d9
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteResourcesSchedulingRule.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core;
+
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The purpose of the WriteResourceSchedulingRule is to prevent concurrent modification
+ * of the sync info of a repository provider as well as the resources within the provider's
+ * project. When a write resource rule is held, no other
+ * RepositoryProviderSchedulingRules will be allowed access until the write resource rule
+ * is released. Also, no threads requesting a rule on the provider's project or any of is
+ * children will be allwed to run until the write lock is released. 
+ */
+public class WriteResourcesSchedulingRule extends WriteSyncSchedulingRule {
+	public WriteResourcesSchedulingRule(RepositoryProvider provider) {
+		super(provider);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
+	 */
+	public boolean isConflicting(ISchedulingRule rule) {
+		if (getProvider().getProject().contains(rule)) {
+			// a write resource rule conflicts with modifications on the provider's project
+			return true;
+		}
+		return super.isConflicting(rule);
+	}
+
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java
new file mode 100644
index 0000000..6bb2786
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/WriteSyncSchedulingRule.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core;
+
+
+/**
+ * The purpose of the WriteSyncSchedulingRule is to prevent concurrent modification
+ * of the sync info of a repository provider. When a write sync rule is held, no other
+ * RepositoryProviderSchedulingRules will be allowed access until the write sync rule
+ * is released. However, the write sync rule does not prevent the thread that holds the lock
+ * from modifying the workspace. This is important since the sync info may be contained in 
+ * resources or in the ISynchronizer which noth require write access to the workspace tree. 
+ */
+public class WriteSyncSchedulingRule extends RepositoryProviderSchedulingRule {
+	public WriteSyncSchedulingRule(RepositoryProvider provider) {
+		super(provider);
+	}
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java
index 9046b4f..01c5f33 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/RemoteTagSynchronizer.java
@@ -19,12 +19,15 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.core.internal.jobs.JobManager;
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.Path;
+import org.eclipse.team.core.ReadOnlySchedulingRule;
+import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.core.TeamException;
 import org.eclipse.team.core.sync.IRemoteResource;
 import org.eclipse.team.internal.ccvs.core.CVSException;
@@ -232,19 +235,29 @@
 			for (int i = 0; i < resources.length; i++) {
 				IResource resource = resources[i];
 				
-				monitor.setTaskName(Policy.bind("RemoteTagSynchronizer.0", resource.getFullPath().makeRelative().toString())); //$NON-NLS-1$
-				
-				// build the remote tree only if an initial tree hasn't been provided
-				IRemoteResource	tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70));
-				
-				// update the known remote handles 
-				IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30);
+				boolean lockObtained = false;
 				try {
-					sub.beginTask(null, 512);
-					//removeSyncBytes(resource, IResource.DEPTH_INFINITE);
-					collectChanges(resource, tree, depth, sub);
+					lockObtained = beginRefresh(resource);
+					if (lockObtained) {
+						monitor.setTaskName(Policy.bind("RemoteTagSynchronizer.0", resource.getFullPath().makeRelative().toString())); //$NON-NLS-1$
+						
+						// build the remote tree only if an initial tree hasn't been provided
+						IRemoteResource	tree = buildRemoteTree(resource, depth, cacheFileContentsHint, Policy.subMonitorFor(monitor, 70));
+						
+						// update the known remote handles 
+						IProgressMonitor sub = Policy.infiniteSubMonitorFor(monitor, 30);
+						try {
+							sub.beginTask(null, 512);
+							//removeSyncBytes(resource, IResource.DEPTH_INFINITE);
+							collectChanges(resource, tree, depth, sub);
+						} finally {
+							sub.done();	 
+						}
+					}
 				} finally {
-					sub.done();	 
+					if (lockObtained) {
+						endRefresh();
+					}
 				}
 			}
 		} finally {
@@ -255,6 +268,29 @@
 		return changes;
 	}
 
+	/*
+	 * Obtain a rea-only scheduling rule for the given resource.
+	 * Return true if the rule was obtained and false otherwise.
+	 * If false is returned, do not refresh the resource but continue
+	 * with the others.
+	 */
+	private boolean beginRefresh(IResource resource) {
+		RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject());
+		if (provider == null) return false;
+		JobManager.getInstance().beginRule(new ReadOnlySchedulingRule(provider));
+		// It's possible that the provider was unmapped before we obtained the rule
+		RepositoryProvider newProvider = RepositoryProvider.getProvider(resource.getProject());
+		if (newProvider != provider) {
+			JobManager.getInstance().endRule();
+			return false;
+		}
+		return true;
+	}
+
+	private void endRefresh() {
+		JobManager.getInstance().endRule();
+	}
+
 	/**
 	 * Build a remote tree for the given parameters.
 	 */
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java
index 6a2f7c8..1443e9c 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CheckoutProjectOperation.java
@@ -32,6 +32,7 @@
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.WriteResourcesSchedulingRule;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
 import org.eclipse.team.internal.ccvs.core.CVSStatus;
@@ -168,13 +169,32 @@
 	}
 
 	private ISchedulingRule getSchedulingRule(IProject[] projects) {
-		if (projects.length == 1) {
-			return projects[0];
+		ISchedulingRule[] rules = getSchedulingRules(projects);
+		if (rules.length == 1) {
+			return rules[0];
 		} else {
-			return new MultiRule(projects);
+			return new MultiRule(rules);
 		}
 	}
 
+	/*
+	 * Use a provider scheduling rule for those projects that are mapped to
+	 * a provider.
+	 */
+	private ISchedulingRule[] getSchedulingRules(IProject[] projects) {
+		ISchedulingRule[] rules = new ISchedulingRule[projects.length];
+		for (int i = 0; i < projects.length; i++) {
+			IProject project = projects[i];
+			RepositoryProvider provider = RepositoryProvider.getProvider(project);
+			if (provider != null) {
+				rules[i] = new WriteResourcesSchedulingRule(provider);
+			} else {
+				rules[i] = project;
+			}
+		}
+		return rules;
+	}
+
 	private IStatus performCheckout(Session session, ICVSRemoteFolder resource, IProject[] targetProjects, boolean sendModuleName, IProgressMonitor pm) throws CVSException {
 		// Set the task name of the progress monitor to let the user know
 		// which project we're on. Don't use subTask since that will be
@@ -383,7 +403,6 @@
 	}
 
 	protected String getOverwritePromptMessage(ICVSRemoteFolder remoteFolder, IProject project) {
-		File localLocation  = getFileLocation(project);
 		if(project.exists()) {
 			return Policy.bind("CheckoutOperation.thisResourceExists", project.getName(), getRemoteModuleName(remoteFolder));//$NON-NLS-1$
 		} else {
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java
index 5b8f071..3854432 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/RepositoryProviderOperation.java
@@ -17,12 +17,15 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.core.internal.jobs.JobManager;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.WriteResourcesSchedulingRule;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
 import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
@@ -36,7 +39,7 @@
  * Performs a cvs operation on multiple repository providers
  */
 public abstract class RepositoryProviderOperation extends CVSOperation {
-
+	
 	private IResource[] resources;
 
 	/**
@@ -60,7 +63,12 @@
 			CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
 			List list = (List)table.get(provider);
 			IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
-			execute(provider, providerResources, subMonitor);
+			try {
+				JobManager.getInstance().beginRule(getSchedulingRule(provider));
+				execute(provider, providerResources, subMonitor);
+			} finally {
+				JobManager.getInstance().endRule();
+			}
 		}
 
 	}
@@ -69,16 +77,16 @@
 	 * Helper method. Return a Map mapping provider to a list of resources
 	 * shared with that provider.
 	 */
-	private Map getProviderMapping(IResource[] resources) {
+	private Map getProviderMapping(IResource[] localResources) {
 		Map result = new HashMap();
-		for (int i = 0; i < resources.length; i++) {
-			RepositoryProvider provider = RepositoryProvider.getProvider(resources[i].getProject(), CVSProviderPlugin.getTypeId());
+		for (int i = 0; i < localResources.length; i++) {
+			RepositoryProvider provider = RepositoryProvider.getProvider(localResources[i].getProject(), CVSProviderPlugin.getTypeId());
 			List list = (List)result.get(provider);
 			if (list == null) {
 				list = new ArrayList();
 				result.put(provider, list);
 			}
-			list.add(resources[i]);
+			list.add(localResources[i]);
 		}
 		return result;
 	}
@@ -107,12 +115,12 @@
 	 * @throws CVSException
 	 * @throws InterruptedException
 	 */
-	protected abstract void execute(CVSTeamProvider provider, IResource[] resources, IProgressMonitor monitor) throws CVSException, InterruptedException;
+	protected abstract void execute(CVSTeamProvider provider, IResource[] localResources, IProgressMonitor monitor) throws CVSException, InterruptedException;
 
-	protected ICVSResource[] getCVSArguments(IResource[] resources) {
-		ICVSResource[] cvsResources = new ICVSResource[resources.length];
+	protected ICVSResource[] getCVSArguments(IResource[] localResources) {
+		ICVSResource[] cvsResources = new ICVSResource[localResources.length];
 		for (int i = 0; i < cvsResources.length; i++) {
-			cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(resources[i]);
+			cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(localResources[i]);
 		}
 		return cvsResources;
 	}
@@ -120,10 +128,10 @@
 	/*
 	 * Get the arguments to be passed to a commit or update
 	 */
-	protected String[] getStringArguments(IResource[] resources) throws CVSException {
-		List arguments = new ArrayList(resources.length);
-		for (int i=0;i<resources.length;i++) {
-			IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1);
+	protected String[] getStringArguments(IResource[] localResources) throws CVSException {
+		List arguments = new ArrayList(localResources.length);
+		for (int i=0;i<localResources.length;i++) {
+			IPath cvsPath = localResources[i].getFullPath().removeFirstSegments(1);
 			if (cvsPath.segmentCount() == 0) {
 				arguments.add(Session.CURRENT_LOCAL_FOLDER);
 			} else {
@@ -151,4 +159,14 @@
 		return workspaceRoot.getLocalRoot();
 	}
 	
+	/**
+	 * Return the scheduling rule to be used for the given provider. The default is
+	 * write access to both sync info and resources. Subclasses may override.
+	 * @param provider the repository provider
+	 * @return the scheduling rule to be used for this operation on the provider
+	 */
+	protected ISchedulingRule getSchedulingRule(RepositoryProvider provider) {
+		return new WriteResourcesSchedulingRule(provider);
+	}
+	
 }
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java
index 87e22bf..19ae11a 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/TagOperation.java
@@ -16,8 +16,10 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.WriteSyncSchedulingRule;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSTag;
 import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
@@ -57,7 +59,6 @@
 	 */
 	protected String getErrorMessage(IStatus[] problems, int operationCount) {
 		// We accumulated 1 status per resource above.
-		MultiStatus combinedStatus;
 		if(operationCount == 1) {
 			return Policy.bind("TagAction.tagProblemsMessage"); //$NON-NLS-1$
 		} else {
@@ -124,4 +125,12 @@
 	protected  String getTaskName() {
 		return Policy.bind("TagFromWorkspace.taskName"); //$NON-NLS-1$
 	}
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation#getSchedulingRule(org.eclipse.team.core.RepositoryProvider)
+	 */
+	protected ISchedulingRule getSchedulingRule(RepositoryProvider provider) {
+		// The tag operation writes sync info
+		return new WriteSyncSchedulingRule(provider);
+	}
+
 }