*** empty log message ***
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 d081a16..df51bc1 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
@@ -316,6 +316,10 @@
 		workspace.removeResourceChangeListener(fileModificationManager);
 		workspace.removeResourceChangeListener(addDeleteMoveListener);
 		
+		// remove all of this plugin's save participants. This is easier than having
+		// each class that added itself as a participant to have to listen to shutdown.
+		workspace.removeSaveParticipant(this);
+		
 		deleteCacheDirectory();
 	}
 		
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
index 63bb0f0..74fcea3 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
@@ -499,7 +499,8 @@
 			EclipseSynchronizer.getInstance().flushDirtyCache(getIResource(), IResource.DEPTH_ZERO);
 			return;
 		}
-		setModified(isModified(getSyncInfo()));
+		//setModified(isModified(getSyncInfo()));
+		flushWithAncestors();
 	}
 	
 	/**
@@ -514,7 +515,18 @@
 	}
 	
 	public boolean handleModification(boolean forAddition) throws CVSException {
-		if (isIgnored()) return false;
+		if (isIgnored()) {			
+			// Special case handling for when a resource passes from the un-managed state
+			// to the ignored state (e.g. ignoring the ignore file). Parent dirty state must be
+			// recalculated but since the resource's end state is ignored there is a lot of code
+			// in the plugin that simply disregards the change to the resource.
+			// There may be a better was of handling resources that transition from un-managed to
+			// ignored but for now this seems like the safest change. 
+			if(! resource.isDerived()) {
+				flushWithAncestors();
+			}
+			return false;
+		} 
 		if (EclipseSynchronizer.getInstance().contentsChangedByUpdate(getIFile()))
 			return false;
 		if (forAddition) {
@@ -528,10 +540,8 @@
 	}
 	
 	/**
-	 * Method setModified sets the modified status of the reciever. This method
+	 * Sets the modified status of the receiver. This method
 	 * returns true if there was a change in the modified status of the file.
-	 * @param iFile
-	 * @param b
 	 */
 	private boolean setModified(boolean modified) throws CVSException {
 		try {
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 265c79d..fe77ff0 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
@@ -354,7 +354,9 @@
 	 * @param b
 	 */
 	protected void adjustModifiedCount(boolean modified) throws CVSException {
-		flushWithAncestors();
+		if (isModified() != modified) {
+			flushWithAncestors();
+		}
 //		if (EclipseSynchronizer.getInstance().adjustModifiedCount((IContainer)getIResource(), modified)) {
 //			((EclipseFolder)getParent()).adjustModifiedCount(modified);
 //		}
@@ -440,6 +442,12 @@
 	private String determineDirtyCount(String indicator, boolean shared) throws CVSException {
 		IContainer container = (IContainer)getIResource();
 		ICVSResource[] children = members(ALL_UNIGNORED_MEMBERS);
+
+		if (Policy.DEBUG_DIRTY_CACHING) {
+			System.out.println("checking isModified() recursively for " //$NON-NLS-1$
+			+getPath());
+		}
+
 		int count = 0;
 		Set deletedChildren = new HashSet();
 		for (int i = 0; i < children.length; i++) {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
index a3300cc..60ad3d4 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseSynchronizer.java
@@ -1280,6 +1280,12 @@
 	 */
 	protected boolean setModified(IResource container, boolean modified) throws CVSException {
 		String indicator = modified ? IS_DIRTY_INDICATOR : NOT_DIRTY_INDICATOR;
+		
+		// DECORATOR this state change check is error prone. it is quite possible that
+		// another thread updated the dirty state (e.g. the decorator) and thus the returned
+		// value from this method will be wrong and lead the caller to make assumptions
+		// that can be wrong. That the state has not changed, but it has just too quickly.
+		
 		// if it's already set, no need to set the property or adjust the parents count
 		if (indicator.equals(getDirtyIndicator(container))) return false;
 		// set the dirty indicator and adjust the parent accordingly
@@ -1406,7 +1412,10 @@
 				}
 				getSyncInfoCacheFor(resource).flushDirtyCache(resource);
 			} finally {
-				flushDirtyCacheWithAncestors(resource.getParent());
+				IContainer parent = resource.getParent();
+				if(! alreadyflushed(parent)) {		
+					flushDirtyCacheWithAncestors(parent);
+			}
 			}
 		} finally {
 			endOperation(null);
@@ -1414,6 +1423,14 @@
 	}
 	
 	/**
+	 * @param parent
+	 * @return boolean
+	 */
+	private boolean alreadyflushed(IContainer resource) throws CVSException {
+		return getSyncInfoCacheFor(resource).isDirtyCacheFlushed(resource);
+	}
+
+	/**
 	 * Method updated flags the objetc as having been modfied by the updated
 	 * handler. This flag is read during the resource delta to determine whether
 	 * the modification made the file dirty or not.
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FileModificationManager.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FileModificationManager.java
index bd20e5e..8f9da85 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FileModificationManager.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FileModificationManager.java
@@ -59,6 +59,9 @@
 						if (!project.isAccessible()) {
 							return false;
 						}
+						if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
+							return false;
+						} 
 						if (RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId()) == null) {
 							return false;
 						}
@@ -168,9 +171,8 @@
 	private void resourceAdded(IResource resource) throws CoreException {
 		try {
 			EclipseResource cvsResource = (EclipseResource)CVSWorkspaceRoot.getCVSResourceFor(resource);
-			if (cvsResource.handleModification(true /* addition */)) {
+			cvsResource.handleModification(true /* addition */);
 				modifiedResources.add(resource);
-			}
 		} catch (CVSException e) {
 			throw e.toCoreException();
 		}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
index fd81e87..c48c508 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
@@ -107,6 +107,7 @@
 		newInfo.setKeywordMode(Command.KSUBST_TEXT_EXPAND);
 		newInfo.setTag(tag);
 		syncBytes = newInfo.getBytes();
+		setWorkspaceSyncState(workspaceSyncState);
 	}
 		
 	public RemoteFile(RemoteFolder parent, byte[] syncBytes) throws CVSException {
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SessionPropertySyncInfoCache.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SessionPropertySyncInfoCache.java
index fb4f969..a4e132c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SessionPropertySyncInfoCache.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SessionPropertySyncInfoCache.java
@@ -15,10 +15,17 @@
 
 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.resources.IResourceVisitor;
+import org.eclipse.core.resources.ISaveContext;
+import org.eclipse.core.resources.ISaveParticipant;
+import org.eclipse.core.resources.ISynchronizer;
+import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
 import org.eclipse.team.internal.ccvs.core.Policy;
@@ -27,17 +34,31 @@
 
 /**
  * This cache uses session properties to hold the bytes representing the sync
- * info
+ * info. In addition when the workbench closes or a project is closed, the dirty 
+ * state for all cvs managed folders are persisted using the resource's plugin
+ * synchronizer.
  */
-/*package*/ class SessionPropertySyncInfoCache extends SyncInfoCache {
+/*package*/ class SessionPropertySyncInfoCache extends SyncInfoCache implements ISaveParticipant {
 	
 	// key used on a folder to indicate that the resource sync has been cahced for it's children
 	private static final QualifiedName RESOURCE_SYNC_CACHED_KEY = new QualifiedName(CVSProviderPlugin.ID, "resource-sync-cached"); //$NON-NLS-1$
+	private static final QualifiedName FOLDER_SYNC_RESTORED_KEY = new QualifiedName(CVSProviderPlugin.ID, "folder-sync-restored"); //$NON-NLS-1$
+	
 	private static final Object RESOURCE_SYNC_CACHED = new Object();
+	private static final Object FOLDER_SYNC_RESTORED = new Object();
 	
 	/*package*/ static final String[] NULL_IGNORES = new String[0];
 	private static final FolderSyncInfo NULL_FOLDER_SYNC_INFO = new FolderSyncInfo("", "", null, false); //$NON-NLS-1$ //$NON-NLS-2$
 	
+	/*package*/ SessionPropertySyncInfoCache() {
+		try {
+			// this save participant is removed when the plugin is shutdown.			
+			ResourcesPlugin.getWorkspace().addSaveParticipant(CVSProviderPlugin.getPlugin(), this);
+		} catch (CoreException e) {
+			CVSProviderPlugin.log(e.getStatus());
+		}
+	}
+	
 	/**
 	 * If not already cached, loads and caches the folder ignores sync for the container.
 	 * Folder must exist and must not be the workspace root.
@@ -206,14 +227,28 @@
 	}
 	private void internalSetDirtyIndicator(IContainer container, String indicator) throws CVSException {
 		try {
-			container.setPersistentProperty(IS_DIRTY, indicator);
+			container.setSessionProperty(IS_DIRTY, indicator);
 		} catch (CoreException e) {
 			throw CVSException.wrapException(e);
 		}
 	}
 	private String internalGetDirtyIndicator(IContainer container) throws CVSException {
 		try {
-			return container.getPersistentProperty(IS_DIRTY);
+			String di = (String)container.getSessionProperty(IS_DIRTY);
+			
+			// if the session property is not available then restore from persisted sync info. At this
+			// time the sync info is not flushed because we don't want the workspace to generate
+			// a delta. Since the sync info is not flushed another session property is used to remember
+			// that the sync info was already converted to a session property and has become stale.			
+			if(di == null && container.getSessionProperty(FOLDER_SYNC_RESTORED_KEY) == null) {
+				byte [] diBytes = ResourcesPlugin.getWorkspace().getSynchronizer().getSyncInfo(RESOURCE_SYNC_KEY, container);
+				if(diBytes != null) {
+					di = new String(diBytes);
+					setDirtyIndicator(container, di);
+				}
+				container.setSessionProperty(FOLDER_SYNC_RESTORED_KEY, FOLDER_SYNC_RESTORED);
+			}
+			return di;
 		} catch (CoreException e) {
 			throw CVSException.wrapException(e);
 		}
@@ -260,7 +295,7 @@
 				} else {
 					resource.setSessionProperty(DIRTY_COUNT, null);
 					resource.setSessionProperty(DELETED_CHILDREN, null);
-					resource.setPersistentProperty(IS_DIRTY, null);
+					resource.setSessionProperty(IS_DIRTY, null);
 				}
 			} catch (CoreException e) {
 				throw CVSException.wrapException(e);
@@ -328,6 +363,10 @@
 	/*package*/ void markFileAsUpdated(IFile file) throws CVSException {
 		try {
 			file.setSessionProperty(CLEAN_UPDATE, UPDATED_INDICATOR);
+			
+			// update dirty state so that decorators don't have to recompute after
+			// an update is completed.
+			contentsChangedByUpdate(file);
 		} catch (CoreException e) {
 			throw CVSException.wrapException(e);
 		}
@@ -394,4 +433,88 @@
 			throw CVSException.wrapException(e);
 		}
 	}
-}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#isDirtyCacheFlushed(org.eclipse.core.resources.IContainer)
+	 */
+	boolean isDirtyCacheFlushed(IContainer resource) throws CVSException {
+		if (resource.exists()) {
+			try {
+					return resource.getSessionProperty(IS_DIRTY) == null;					
+			} catch (CoreException e) {
+				throw CVSException.wrapException(e);
+			}
+		}
+		return false;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext)
+	 */
+	public void doneSaving(ISaveContext context) {
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext)
+	 */
+	public void prepareToSave(ISaveContext context) throws CoreException {
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext)
+	 */
+	public void rollback(ISaveContext context) {			
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext)
+	 */
+	public void saving(ISaveContext context) throws CoreException {
+		boolean fullSave = (context.getKind() == ISaveContext.FULL_SAVE);
+		boolean projectSave = (context.getKind() == ISaveContext.PROJECT_SAVE);
+		
+		if(projectSave || fullSave) {
+			// persist all session properties for folders into sync info.
+			final ISynchronizer synchronizer = ResourcesPlugin.getWorkspace().getSynchronizer();
+			synchronizer.add(RESOURCE_SYNC_KEY);
+		
+			// traverse the workspace looking for CVS managed projects or just the 
+			// specific projects being closed
+			IProject[] projects;
+			if(projectSave) {
+				projects = new IProject[1];
+				projects[0] = context.getProject();
+			} else {
+				projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+			}
+			for (int i = 0; i < projects.length; i++) {
+				IProject project = projects[i];
+				RepositoryProvider provider = RepositoryProvider.getProvider(
+														project,
+														CVSProviderPlugin.getTypeId());
+														
+				// found a project managed by CVS, convert each session property on a
+				// folder to a sync object.
+				if (provider != null) {
+					project.accept(new IResourceVisitor() {
+						public boolean visit(IResource resource) throws CoreException {
+							if(resource.getType() != IResource.FILE) {
+								String di = null;
+								try {
+									di = getDirtyIndicator(resource);
+								} catch (CVSException e) {
+									// continue traversal
+									CVSProviderPlugin.log(e);
+								}
+								if(di != null) {
+									synchronizer.setSyncInfo(RESOURCE_SYNC_KEY, resource, di.getBytes());
+								}								
+							}
+							return true;
+						}
+					});
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SyncInfoCache.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SyncInfoCache.java
index 6d49600..6da5e77 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SyncInfoCache.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SyncInfoCache.java
@@ -141,4 +141,10 @@
 	 * @param container
 	 */
 	/*package*/ abstract void setResourceSyncInfoCached(IContainer container) throws CVSException;
+
+	/**
+	 * @param resource
+	 * @return boolean
+	 */
+	/*package*/ abstract boolean isDirtyCacheFlushed(IContainer resource) throws CVSException;
 }
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SynchronizerSyncInfoCache.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SynchronizerSyncInfoCache.java
index ba1b54c..a312785 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SynchronizerSyncInfoCache.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/SynchronizerSyncInfoCache.java
@@ -449,4 +449,11 @@
 	boolean isFolderSyncInfoCached(IContainer container) throws CVSException {
 		return true;
 	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.team.internal.ccvs.core.resources.SyncInfoCache#isDirtyCacheFlushed(org.eclipse.core.resources.IContainer)
+	 */
+	boolean isDirtyCacheFlushed(IContainer resource) throws CVSException {
+		return false;
+	}
 }
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AddDeleteMoveListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AddDeleteMoveListener.java
index cf53ff9..a01d648 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AddDeleteMoveListener.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AddDeleteMoveListener.java
@@ -201,6 +201,13 @@
 			for (int i = 0; i < projectDeltas.length; i++) {							
 				final IResourceDelta delta = projectDeltas[i];
 				IResource resource = delta.getResource();
+				
+				if (resource.getType() == IResource.PROJECT) {
+					// If the project is not accessible, don't process it
+					if (!resource.isAccessible()) continue;
+					if ((delta.getFlags() & IResourceDelta.OPEN) != 0) continue;
+				}
+				
 				RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());	
 
 				// Make sure that the project is a CVS folder.
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java
index 5f6f546..1eda04c 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileChangeListener.java
@@ -73,6 +73,7 @@
 					if (resource.getType() == IResource.PROJECT) {
 						// If the project is not accessible, don't process it
 						if (!resource.isAccessible()) return false;
+						if ((delta.getFlags() & IResourceDelta.OPEN) != 0) return false;
 					}
 					
 					String name = resource.getName();
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SharingWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SharingWizard.java
index fd1e876..660284a 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SharingWizard.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/SharingWizard.java
@@ -431,9 +431,8 @@
 						// for now, just unmanage
 						folder.unmanage(null);
 					}
-					folder.acceptChildren(this);
 				}
-			});
+			}, true /* recurse */);
 		} catch (CVSException e) {
 			// log the exception and return null
 			CVSUIPlugin.log(e);