@@ -21,11 +21,17 @@
 using Microsoft.VisualStudio.Project;
 using Microsoft.VisualStudio.Shell.Interop;
 using System.Windows.Forms;
+using System.Collections.Generic;
 
 namespace Microsoft.IronStudio.Project {
     public abstract class DirectoryBasedProjectNode : CommonProjectNode {
+        private Dispatcher _dispatcher;
         private FileSystemWatcher _projectWatcher;
 
+        private static readonly TimeSpan _fileChangeThrottleInterval = TimeSpan.FromSeconds(0.2);
+        private DispatcherTimer _timer = null;
+        private readonly Dictionary<string, Action> _pendingFileUpdates = new Dictionary<string, Action>();
+
         public DirectoryBasedProjectNode(CommonProjectPackage package, ImageList imageList)
             : base(package, imageList) {
         }
@@ -101,9 +107,13 @@ public DirectoryBasedProjectNode(CommonProjectPackage package, ImageList imageLi
                 _projectWatcher.Deleted -= new FileSystemEventHandler(FileDeleted);
             }
 
+            if (_dispatcher == null) {
+                _dispatcher = Dispatcher.CurrentDispatcher;
+            }
+
             _projectWatcher = new FileSystemWatcher(ProjectDir);
             _projectWatcher.IncludeSubdirectories = true;
-            _projectWatcher.SynchronizingObject = new SynchronizingInvoke(Dispatcher.CurrentDispatcher);
+            _projectWatcher.SynchronizingObject = new SynchronizingInvoke(_dispatcher);
 
             _projectWatcher.Created += FileCreated;
             _projectWatcher.Deleted += FileDeleted;
@@ -112,46 +122,100 @@ public DirectoryBasedProjectNode(CommonProjectPackage package, ImageList imageLi
             _projectWatcher.EnableRaisingEvents = true;
         }
 
-        private void FileDeleted(object sender, FileSystemEventArgs e) {
-            Debug.Assert(e.ChangeType == WatcherChangeTypes.Deleted);
+        private Action CreateSafeCallback(FileSystemEventArgs notification, Action callback) {
+            Predicate<string> exists = (s) => File.Exists(s) || Directory.Exists(s);
+
+            switch (notification.ChangeType) {
+                case WatcherChangeTypes.Created: // ignore rapid add/delete
+                    return () => {
+                        if (exists(notification.FullPath))
+                            callback();
+                    };
+                case WatcherChangeTypes.Deleted: // ignore rapid delete/re-add
+                case WatcherChangeTypes.Renamed: // ignore rapid rename/revert
+                    return () => {
+                        if (!exists(notification.FullPath))
+                            callback();
+                    };
+                default:
+                    throw new NotSupportedException(String.Format("CreateSafeCallback is not supported for change type {0}", notification.ChangeType));
+            };
+        }
+
+        // Problem: Many applications (including visual studio itself) will save a file by deleting and re-creating it.
+        // If we issue a Delete/Recreate, then source control providers will pend a delete on the file.
+        // This is particularly nasty when merging changes to files just before a checkin, and can lead to accidentally pending
+        // deletes for files you're trying to edit.
+        // Solution: Throttle file system notifications, and only delete the file if it is actually gone some interval (say 0.2 seconds) later
+        private void ThrottleFileUpdate(FileSystemEventArgs notification, Action callback) {
+            _dispatcher.VerifyAccess();
+
+            // we only worry about keeping the single last operation for any given file
+            _pendingFileUpdates[notification.FullPath] = CreateSafeCallback(notification, callback);
 
-            HierarchyNode child = FindChild(e.FullPath);
-            if (child != null) {
-                // TODO: We shouldn't be closing any documents, we probably need to pass a flag in here.
-                // Unfortunately it's not really simple because when we remove the child from the parent
-                // the file is no longer savable if it's already open.  So for now the file just simply
-                // disappears - deleting it from the file system means you better want it gone from
-                // the editor as well.
-                child.Remove(false);
+            if(_timer != null) {
+                _timer.Stop();
             }
+
+            _timer = new DispatcherTimer(_fileChangeThrottleInterval, DispatcherPriority.Normal, (s, e) => {
+                _dispatcher.VerifyAccess();
+                _timer.Stop(); // don't repeat
+
+                // flush all the pending updates.
+                var copy = new List<Action>(_pendingFileUpdates.Values);
+                _pendingFileUpdates.Clear();
+
+                foreach (var t in copy) {
+                    callback();
+                }
+            }, _dispatcher);
+            _timer.Start();
+        }
+
+        private void FileDeleted(object sender, FileSystemEventArgs e) {
+            Debug.Assert(e.ChangeType == WatcherChangeTypes.Deleted);
+            ThrottleFileUpdate(e, () => {
+                HierarchyNode child = FindChild(e.FullPath);
+                if (child != null) {
+                    // TODO: We shouldn't be closing any documents, we probably need to pass a flag in here.
+                    // Unfortunately it's not really simple because when we remove the child from the parent
+                    // the file is no longer savable if it's already open.  So for now the file just simply
+                    // disappears - deleting it from the file system means you better want it gone from
+                    // the editor as well.
+                    child.Remove(false);
+                }
+            });
         }
 
         private void FileRenamed(object sender, RenamedEventArgs e) {
             Debug.Assert(e.ChangeType == WatcherChangeTypes.Renamed);
 
-            var child = FindChild(e.OldFullPath);
-            if (child != null) {
-                FileNode fileNode = child as FileNode;
-                if (fileNode != null) {
-                    // file nodes could be open so we'll need to update any
-                    // state such as the file caption
-                    try {
-                        fileNode.RenameDocument(e.OldFullPath, e.FullPath);
-                    } catch (Exception) {
+            ThrottleFileUpdate(e, () => {
+
+                var child = FindChild(e.OldFullPath);
+                if (child != null) {
+                    FileNode fileNode = child as FileNode;
+                    if (fileNode != null) {
+                        // file nodes could be open so we'll need to update any
+                        // state such as the file caption
+                        try {
+                            fileNode.RenameDocument(e.OldFullPath, e.FullPath);
+                        } catch (Exception) {
+                        }
                     }
-                }
 
-                FolderNode folderNode = child as FolderNode;
-                if (folderNode != null) {
-                    try {
-                        folderNode.RenameFolder(e.FullPath);
-                    } catch (Exception) {
+                    FolderNode folderNode = child as FolderNode;
+                    if (folderNode != null) {
+                        try {
+                            folderNode.RenameFolder(e.FullPath);
+                        } catch (Exception) {
+                        }
                     }
-                }
 
-                child.ItemNode.Rename(e.FullPath);
-                child.OnInvalidateItems(child.Parent);
-            }
+                    child.ItemNode.Rename(e.FullPath);
+                    child.OnInvalidateItems(child.Parent);
+                }
+            });
         }
 
         private void FileCreated(object sender, FileSystemEventArgs e) {
@@ -162,31 +226,34 @@ public DirectoryBasedProjectNode(CommonProjectPackage package, ImageList imageLi
 
             Debug.Assert(e.ChangeType == WatcherChangeTypes.Created);
 
-            // find the parent where the new node will be inserted
-            HierarchyNode parent;
-            string path = e.FullPath;
-            for (; ; ) {
-                string dir = Path.GetDirectoryName(path);
-                if (NativeMethods.IsSamePath(dir, ProjectDir)) {
-                    parent = this;
-                    break;
-                }
+            ThrottleFileUpdate(e, () => {
+
+                // find the parent where the new node will be inserted
+                HierarchyNode parent;
+                string path = e.FullPath;
+                for (; ; ) {
+                    string dir = Path.GetDirectoryName(path);
+                    if (NativeMethods.IsSamePath(dir, ProjectDir)) {
+                        parent = this;
+                        break;
+                    }
 
-                parent = FindChild(dir);
-                if (parent == null) {
-                    path = dir;
-                } else {
-                    break;
+                    parent = FindChild(dir);
+                    if (parent == null) {
+                        path = dir;
+                    } else {
+                        break;
+                    }
                 }
-            }
 
-            // and then insert either a file or directory node
-            FileInfo fi = new FileInfo(e.FullPath);
-            if ((fi.Attributes & FileAttributes.Directory) != 0) {
-                AddDirectory(parent, false, path);
-            } else if (ShouldIncludeFileInProject(GetProjectFileExtension(), e.FullPath)) {
-                AddFile(parent, false, e.FullPath);
-            }
+                // and then insert either a file or directory node
+                FileInfo fi = new FileInfo(e.FullPath);
+                if ((fi.Attributes & FileAttributes.Directory) != 0) {
+                    AddDirectory(parent, false, path);
+                } else if (ShouldIncludeFileInProject(GetProjectFileExtension(), e.FullPath)) {
+                    AddFile(parent, false, e.FullPath);
+                }
+            });
         }
 
         /// <summary>
