diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index 15008a72cc5a13a8fa7e86c75720d77bf6e9a21f..8e06c85175884868ab0572e91326ff9ce80e52a9 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -578,9 +578,9 @@ Core::Internal::EditorView *EditorManager::currentEditorView() const
 QList<IEditor *> EditorManager::editorsForFileName(const QString &filename) const
 {
     QList<IEditor *> found;
-    QString fixedname = FileManager::fixFileName(filename);
+    QString fixedname = FileManager::fixFileName(filename, FileManager::KeepLinks);
     foreach (IEditor *editor, openedEditors()) {
-        if (fixedname == FileManager::fixFileName(editor->file()->fileName()))
+        if (fixedname == FileManager::fixFileName(editor->file()->fileName(), FileManager::KeepLinks))
             found << editor;
     }
     return found;
diff --git a/src/plugins/coreplugin/filemanager.cpp b/src/plugins/coreplugin/filemanager.cpp
index ba9c52069767c27e5d3f86d803da4bb8b40690b8..d7f18179d99befc6c17d162067e054dff6c0f89a 100644
--- a/src/plugins/coreplugin/filemanager.cpp
+++ b/src/plugins/coreplugin/filemanager.cpp
@@ -112,6 +112,7 @@ struct FileManagerPrivate {
     QMap<QString, FileState> m_states;
     QStringList m_changedFiles;
     QList<IFile *> m_filesWithoutWatch;
+    QMap<IFile *, QStringList> m_filesWithWatch;
 
     QStringList m_recentFiles;
     static const int m_maxRecentFiles = 7;
@@ -120,6 +121,7 @@ struct FileManagerPrivate {
 
     QMainWindow *m_mainWindow;
     QFileSystemWatcher *m_fileWatcher;
+    QFileSystemWatcher *m_linkWatcher;
     bool m_blockActivated;
     QString m_lastVisitedDirectory;
     QString m_projectsDirectory;
@@ -143,6 +145,16 @@ FileManagerPrivate::FileManagerPrivate(QObject *q, QMainWindow *mw) :
 #endif
     m_blockedIFile(0)
 {
+    q->connect(m_fileWatcher, SIGNAL(fileChanged(QString)),
+        q, SLOT(changedFile(QString)));
+#ifdef Q_OS_UNIX
+    m_linkWatcher = new QFileSystemWatcher(q);
+    m_linkWatcher->setObjectName(QLatin1String("_qt_autotest_force_engine_poller"));
+    q->connect(m_linkWatcher, SIGNAL(fileChanged(QString)),
+        q, SLOT(changedFile(QString)));
+#else
+    m_linkWatcher = m_fileWatcher;
+#endif
 }
 
 } // namespace Internal
@@ -152,8 +164,6 @@ FileManager::FileManager(QMainWindow *mw)
     d(new Internal::FileManagerPrivate(this, mw))
 {
     Core::ICore *core = Core::ICore::instance();
-    connect(d->m_fileWatcher, SIGNAL(fileChanged(QString)),
-        this, SLOT(changedFile(QString)));
     connect(d->m_mainWindow, SIGNAL(windowActivated()),
         this, SLOT(mainWindowActivated()));
     connect(core, SIGNAL(contextChanged(Core::IContext*,Core::Context)),
@@ -173,148 +183,167 @@ FileManager::~FileManager()
     Adds a list of IFile's to the collection. If \a addWatcher is true (the default),
     the files are added to a file system watcher that notifies the file manager
     about file changes.
-
-    Returns true if the file specified by \a files have not been yet part of the file list.
 */
 void FileManager::addFiles(const QList<IFile *> &files, bool addWatcher)
 {
     if (!addWatcher) {
         // We keep those in a separate list
 
-        foreach(IFile *file, files)
-            connect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *)));
-
-        d->m_filesWithoutWatch.append(files);
+        foreach(IFile *file, files) {
+            if (file && !d->m_filesWithoutWatch.contains(file)) {
+                connect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *)));
+                d->m_filesWithoutWatch.append(file);
+            }
+        }
         return;
     }
 
     foreach (IFile *file, files) {
-        if (!file)
-            continue;
-        const QString &fixedFileName = fixFileName(file->fileName());
-        if (d->m_states.value(fixedFileName).lastUpdatedState.contains(file))
-            continue;
-        connect(file, SIGNAL(changed()), this, SLOT(checkForNewFileName()));
-        connect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *)));
-
-        addFileInfo(file);
+        if (file && !d->m_filesWithWatch.contains(file)) {
+            connect(file, SIGNAL(changed()), this, SLOT(checkForNewFileName()));
+            connect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *)));
+            addFileInfo(file);
+        }
     }
 }
 
+/* Adds the IFile's file and possibly it's final link target to both m_states
+   (if it's file name is not empty), and the m_filesWithWatch list,
+   and adds a file watcher for each if not already done.
+   (The added file names are guaranteed to be absolute and cleaned.) */
 void FileManager::addFileInfo(IFile *file)
 {
-    // We do want to insert the IFile into d->m_states even if the filename is empty
-    // Such that m_states always contains all IFiles
-
-    const QString fixedname = fixFileName(file->fileName());
-    Internal::FileStateItem item;
-    if (!fixedname.isEmpty()) {
-        const QFileInfo fi(file->fileName());
-        item.modified = fi.lastModified();
-        item.permissions = fi.permissions();
-    }
+    const QString fixedName = fixFileName(file->fileName(), KeepLinks);
+    const QString fixedResolvedName = fixFileName(file->fileName(), ResolveLinks);
+    addFileInfo(fixedResolvedName, file, false);
+    if (fixedName != fixedResolvedName)
+        addFileInfo(fixedName, file, true);
+}
 
-    if (!d->m_states.contains(fixedname)) {
-        d->m_states.insert(fixedname, Internal::FileState());
-        if (!fixedname.isEmpty()) {
-            d->m_fileWatcher->addPath(fixedname);
+/* only called from addFileInfo(IFile *) */
+void FileManager::addFileInfo(const QString &fileName, IFile *file, bool isLink)
+{
+    Internal::FileStateItem state;
+    if (!fileName.isEmpty()) {
+        const QFileInfo fi(fileName);
+        state.modified = fi.lastModified();
+        state.permissions = fi.permissions();
+        // Add watcher if we don't have that already
+        if (!d->m_states.contains(fileName)) {
+            d->m_states.insert(fileName, Internal::FileState());
+
+            if (isLink)
+                d->m_linkWatcher->addPath(fileName);
+            else
+                d->m_fileWatcher->addPath(fileName);
         }
+        d->m_states[fileName].lastUpdatedState.insert(file, state);
     }
-
-    d->m_states[fixedname].lastUpdatedState.insert(file, item);
+    d->m_filesWithWatch[file].append(fileName); // inserts a new QStringList if not already there
 }
 
+/* Updates the time stamp and permission information of the files
+   registered for this IFile (in m_filesWithWatch; can be the IFile's file + final link target) */
 void FileManager::updateFileInfo(IFile *file)
 {
-    const QString fixedname = fixFileName(file->fileName());
-    // If the filename is empty there's nothing to do
-    if (fixedname.isEmpty())
-        return;
-    const QFileInfo fi(file->fileName());
-
-    Internal::FileStateItem item;
-    item.modified = fi.lastModified();
-    item.permissions = fi.permissions();
-
-    if (d->m_states.contains(fixedname) && d->m_states.value(fixedname).lastUpdatedState.contains(file))
-        d->m_states[fixedname].lastUpdatedState.insert(file, item);
+    foreach (const QString &fileName, d->m_filesWithWatch.value(file)) {
+        // If the filename is empty there's nothing to do
+        if (fileName.isEmpty())
+            continue;
+        const QFileInfo fi(fileName);
+        Internal::FileStateItem item;
+        item.modified = fi.lastModified();
+        item.permissions = fi.permissions();
+        QTC_ASSERT(d->m_states.contains(fileName), continue);
+        QTC_ASSERT(d->m_states.value(fileName).lastUpdatedState.contains(file), continue);
+        d->m_states[fileName].lastUpdatedState.insert(file, item);
+    }
 }
 
 /// Dumps the state of the file manager's map
 /// For debugging purposes
 void FileManager::dump()
 {
+    qDebug() << "======== dumping state map";
     QMap<QString, Internal::FileState>::const_iterator it, end;
     it = d->m_states.constBegin();
     end = d->m_states.constEnd();
     for (; it != end; ++it) {
-        qDebug()<<" ";
         qDebug() << it.key();
-        qDebug() << it.value().expected.modified;
+        qDebug() << "   expected:" << it.value().expected.modified;
 
         QMap<IFile *, Internal::FileStateItem>::const_iterator jt, jend;
         jt = it.value().lastUpdatedState.constBegin();
         jend = it.value().lastUpdatedState.constEnd();
         for (; jt != jend; ++jt) {
-            qDebug() << jt.key() << jt.value().modified;
+            qDebug() << "  " << jt.key()->fileName() << jt.value().modified;
         }
     }
+    qDebug() << "------- dumping files with watch list";
+    foreach (IFile *key, d->m_filesWithWatch.keys()) {
+        qDebug() << key->fileName() << d->m_filesWithWatch.value(key);
+    }
+    qDebug() << "------- dumping watch list";
+    qDebug() << d->m_fileWatcher->files();
+    qDebug() << "------- dumping link watch list";
+    qDebug() << d->m_linkWatcher->files();
 }
 
+/*!
+    \fn void FileManager::renamedFile(const QString &from, QString &to)
+    \brief Tells the file manager that a file has been renamed on disk from within Qt Creator.
+
+    Needs to be called right after the actual renaming on disk (i.e. before the file system
+    watcher can report the event during the next event loop run). \a from needs to be an absolute file path.
+    This will notify all IFile objects pointing to that file of the rename
+    by calling IFile::rename, and update the cached time and permission
+    information to avoid annoying the user with "file has been removed"
+    popups.
+*/
 void FileManager::renamedFile(const QString &from, QString &to)
 {
-    QString fixedFrom = fixFileName(from);
-    QString fixedTo = fixFileName(to);
-    if (d->m_states.contains(fixedFrom)) {
-        QTC_ASSERT(!d->m_states.contains(to), return);
-        d->m_states.insert(fixedTo, d->m_states.value(fixedFrom));
-        d->m_states.remove(fixedFrom);
-        QFileInfo fi(to);
-        d->m_states[fixedTo].expected.modified = fi.lastModified();
-        d->m_states[fixedTo].expected.permissions = fi.permissions();
-
-        d->m_fileWatcher->removePath(fixedFrom);
-        d->m_fileWatcher->addPath(fixedTo);
-
-        QMap<IFile *, Internal::FileStateItem>::iterator it, end;
-        it = d->m_states[fixedTo].lastUpdatedState.begin();
-        end = d->m_states[fixedTo].lastUpdatedState.end();
-
-        for ( ; it != end; ++it) {
-            d->m_blockedIFile = it.key();
-            it.key()->rename(to);
-            d->m_blockedIFile = it.key();
-            it.value().modified = fi.lastModified();
-        }
-    }
-}
+    const QString &fixedFrom = fixFileName(from, KeepLinks);
 
-///
-/// Does not use file->fileName, as such is save to use
-/// with renamed files and deleted files
-void FileManager::removeFileInfo(IFile *file)
-{
-    QMap<QString, Internal::FileState>::const_iterator it, end;
-    end = d->m_states.constEnd();
-    for (it = d->m_states.constBegin(); it != end; ++it) {
-        if (it.value().lastUpdatedState.contains(file)) {
-            removeFileInfo(it.key(), file);
-            break;
-        }
+    // gather the list of IFiles
+    QList<IFile *> filesToRename;
+    QMapIterator<IFile *, QStringList> it(d->m_filesWithWatch);
+    while (it.hasNext()) {
+        it.next();
+        if (it.value().contains(fixedFrom))
+            filesToRename.append(it.key());
     }
 
+    // rename the IFiles
+    foreach (IFile *file, filesToRename) {
+        d->m_blockedIFile = file;
+        removeFileInfo(file);
+        file->rename(to);
+        addFileInfo(file);
+        d->m_blockedIFile = 0;
+    }
 }
 
-/* only called from removeFileInfo(IFile*) */
-void FileManager::removeFileInfo(const QString &fileName, IFile *file)
+/* Removes all occurrances of the IFile from m_filesWithWatch and m_states.
+   If that results in a file no longer being referenced by any IFile, this
+   also removes the file watcher.
+*/
+void FileManager::removeFileInfo(IFile *file)
 {
-    QTC_ASSERT(d->m_states.value(fileName).lastUpdatedState.contains(file), return);
-    d->m_states[fileName].lastUpdatedState.remove(file);
-    if (d->m_states.value(fileName).lastUpdatedState.isEmpty()) {
-        if (!fileName.isEmpty())
-            d->m_fileWatcher->removePath(fileName);
-        d->m_states.remove(fileName); // this deletes fileName
+    if (!d->m_filesWithWatch.contains(file))
+        return;
+    foreach (const QString &fileName, d->m_filesWithWatch.value(file)) {
+        if (!d->m_states.contains(fileName))
+            continue;
+        d->m_states[fileName].lastUpdatedState.remove(file);
+        if (d->m_states.value(fileName).lastUpdatedState.isEmpty()) {
+            if (d->m_fileWatcher->files().contains(fileName))
+                d->m_fileWatcher->removePath(fileName);
+            if (d->m_linkWatcher->files().contains(fileName))
+                d->m_linkWatcher->removePath(fileName);
+            d->m_states.remove(fileName);
+        }
     }
+    d->m_filesWithWatch.remove(file);
 }
 
 /*!
@@ -323,8 +352,6 @@ void FileManager::removeFileInfo(const QString &fileName, IFile *file)
     Adds a IFile object to the collection. If \a addWatcher is true (the default),
     the file is added to a file system watcher that notifies the file manager
     about file changes.
-
-    Returns true if the file specified by \a file has not been yet part of the file list.
 */
 void FileManager::addFile(IFile *file, bool addWatcher)
 {
@@ -333,7 +360,6 @@ void FileManager::addFile(IFile *file, bool addWatcher)
 
 void FileManager::fileDestroyed(QObject *obj)
 {
-    // removeFileInfo works even if the file does not really exist anymore
     IFile *file = static_cast<IFile*>(obj);
     // Check the special unwatched first:
     if (d->m_filesWithoutWatch.contains(file)) {
@@ -352,8 +378,7 @@ void FileManager::fileDestroyed(QObject *obj)
 */
 void FileManager::removeFile(IFile *file)
 {
-    if (!file)
-        return;
+    QTC_ASSERT(file, return);
 
     // Special casing unwatched files
     if (d->m_filesWithoutWatch.contains(file)) {
@@ -362,12 +387,13 @@ void FileManager::removeFile(IFile *file)
         return;
     }
 
+    removeFileInfo(file);
     disconnect(file, SIGNAL(changed()), this, SLOT(checkForNewFileName()));
     disconnect(file, SIGNAL(destroyed(QObject *)), this, SLOT(fileDestroyed(QObject *)));
-
-    removeFileInfo(file);
 }
 
+/* Slot reacting on IFile::changed. We need to check if the signal was sent
+   because the file was saved under different name. */
 void FileManager::checkForNewFileName()
 {
     IFile *file = qobject_cast<IFile *>(sender());
@@ -376,32 +402,33 @@ void FileManager::checkForNewFileName()
     if (file == d->m_blockedIFile)
         return;
     QTC_ASSERT(file, return);
-    const QString &fileName = fixFileName(file->fileName());
-
-    // check if the IFile is in the map
-    if (d->m_states.value(fileName).lastUpdatedState.contains(file)) {
-        // the file might have been deleted and written again, so guard against that
-        d->m_fileWatcher->removePath(fileName);
-        d->m_fileWatcher->addPath(fileName);
-        updateFileInfo(file);
-        return;
-    }
+    QTC_ASSERT(d->m_filesWithWatch.contains(file), return);
 
-    // Probably the name has changed...
+    // Maybe the name has changed or file has been deleted and created again ...
     // This also updates the state to the on disk state
     removeFileInfo(file);
     addFileInfo(file);
 }
 
-// TODO Rename to nativeFileName
-QString FileManager::fixFileName(const QString &fileName)
+/*!
+    \fn QString FileManager::fixFileName(const QString &fileName, FixMode fixmode)
+    Returns a guaranteed cleaned path in native form. If the file exists,
+    it will either be a cleaned absolute file path (fixmode == KeepLinks), or
+    a cleaned canonical file path (fixmode == ResolveLinks).
+*/
+QString FileManager::fixFileName(const QString &fileName, FixMode fixmode)
 {
     QString s = fileName;
     QFileInfo fi(s);
-    if (!fi.exists())
-        s = QDir::toNativeSeparators(s);
-    else
-        s = QDir::toNativeSeparators(fi.canonicalFilePath());
+    if (fi.exists()) {
+        if (fixmode == ResolveLinks)
+            s = fi.canonicalFilePath();
+        else
+            s = QDir::cleanPath(fi.absoluteFilePath());
+    } else {
+        s = QDir::cleanPath(s);
+    }
+    s = QDir::toNativeSeparators(s);
 #ifdef Q_OS_WIN
     s = s.toLower();
 #endif
@@ -417,16 +444,11 @@ QList<IFile *> FileManager::modifiedFiles() const
 {
     QList<IFile *> modifiedFiles;
 
-    QMap<QString, Internal::FileState>::const_iterator it, end;
-    end = d->m_states.constEnd();
-    for(it = d->m_states.constBegin(); it != end; ++it) {
-        QMap<IFile *, Internal::FileStateItem>::const_iterator jt, jend;
-        jt = (*it).lastUpdatedState.constBegin();
-        jend = (*it).lastUpdatedState.constEnd();
-        for( ; jt != jend; ++jt)
-            if (jt.key()->isModified())
-                modifiedFiles << jt.key();
+    foreach (IFile *file, d->m_filesWithWatch.keys()) {
+        if (file->isModified())
+            modifiedFiles << file;
     }
+
     foreach(IFile *file, d->m_filesWithoutWatch) {
         if (file->isModified())
             modifiedFiles << file;
@@ -464,7 +486,8 @@ void FileManager::unblockFileChange(IFile *file)
     // If so then it's a expected change
 
     updateFileInfo(file);
-    updateExpectedState(fixFileName(file->fileName()));
+    foreach (const QString &fileName, d->m_filesWithWatch.value(file))
+        updateExpectedState(fileName);
 }
 
 /*!
@@ -494,18 +517,24 @@ void FileManager::unexpectFileChange(const QString &fileName)
     // is the same as the saved one here
     // If so then it's a expected change
 
-    updateExpectedState(fileName);
+    if (fileName.isEmpty())
+        return;
+    const QString fixedName = fixFileName(fileName, KeepLinks);
+    updateExpectedState(fixedName);
+    const QString fixedResolvedName = fixFileName(fileName, ResolveLinks);
+    if (fixedName != fixedResolvedName)
+        updateExpectedState(fixedResolvedName);
 }
 
+/* only called from unblock and unexpect file change methods */
 void FileManager::updateExpectedState(const QString &fileName)
 {
-    const QString &fixedName = fixFileName(fileName);
-    if (fixedName.isEmpty())
+    if (fileName.isEmpty())
         return;
-    QFileInfo fi(fixedName);
-    if (d->m_states.contains(fixedName)) {
-        d->m_states[fixedName].expected.modified = fi.lastModified();
-        d->m_states[fixedName].expected.permissions = fi.permissions();
+    if (d->m_states.contains(fileName)) {
+        QFileInfo fi(fileName);
+        d->m_states[fileName].expected.modified = fi.lastModified();
+        d->m_states[fileName].expected.permissions = fi.permissions();
     }
 }
 
@@ -769,9 +798,8 @@ void FileManager::changedFile(const QString &fileName)
 {
     const bool wasempty = d->m_changedFiles.isEmpty();
 
-    const QString &fixedName = fixFileName(fileName);
-    if (!d->m_changedFiles.contains(fixedName))
-        d->m_changedFiles.append(fixedName);
+    if (!d->m_changedFiles.contains(fileName) && d->m_states.contains(fileName))
+        d->m_changedFiles.append(fileName);
 
     if (wasempty && !d->m_changedFiles.isEmpty()) {
         QTimer::singleShot(200, this, SLOT(checkForReload()));
@@ -788,6 +816,8 @@ void FileManager::mainWindowActivated()
 
 void FileManager::checkForReload()
 {
+    if (d->m_changedFiles.isEmpty())
+        return;
     if (QApplication::activeWindow() != d->m_mainWindow)
         return;
 
@@ -801,120 +831,154 @@ void FileManager::checkForReload()
 
     QList<IEditor*> editorsToClose;
     QMap<IFile*, QString> filesToSave;
-    QStringList modifiedFileNames;
+
+    // collect file information
+    QMap<QString, Internal::FileStateItem> currentStates;
+    QMap<QString, IFile::ChangeType> changeTypes;
+    QSet<IFile *> changedIFiles;
     foreach (const QString &fileName, d->m_changedFiles) {
-        // Get the information from the filesystem
-        IFile::ChangeTrigger behavior = IFile::TriggerExternal;
         IFile::ChangeType type = IFile::TypeContents;
+        Internal::FileStateItem state;
         QFileInfo fi(fileName);
         if (!fi.exists()) {
             type = IFile::TypeRemoved;
         } else {
-            modifiedFileNames << fileName;
-            if (fi.lastModified() == d->m_states.value(fileName).expected.modified
-                && fi.permissions() == d->m_states.value(fileName).expected.permissions) {
-                behavior = IFile::TriggerInternal;
-            }
+            state.modified = fi.lastModified();
+            state.permissions = fi.permissions();
         }
+        currentStates.insert(fileName, state);
+        changeTypes.insert(fileName, type);
+        foreach (IFile *file, d->m_states.value(fileName).lastUpdatedState.keys())
+            changedIFiles.insert(file);
+    }
+
+    // handle the IFiles
+    foreach (IFile *file, changedIFiles) {
+        IFile::ChangeTrigger behavior = IFile::TriggerInternal;
+        IFile::ChangeType type = IFile::TypePermissions;
+        bool changed = false;
+        // find out the type & behavior from the two possible files
+        // behavior is internal if all changes are expected (and none removed)
+        // type is "max" of both types (remove > contents > permissions)
+        foreach (const QString & fileName, d->m_filesWithWatch.value(file)) {
+            // was the file reported?
+            if (!currentStates.contains(fileName))
+                continue;
 
-        const QMap<IFile *, Internal::FileStateItem> &lastUpdated =
-                d->m_states.value(fileName).lastUpdatedState;
-        QMap<IFile *, Internal::FileStateItem>::const_iterator it, end;
-        it = lastUpdated.constBegin();
-        end = lastUpdated.constEnd();
-        for ( ; it != end; ++it) {
-            IFile *file = it.key();
-            d->m_blockedIFile = file;
-            // Compare
-            if (it.value().modified == fi.lastModified()
-                && it.value().permissions == fi.permissions()) {
-                // Already up to date
+            Internal::FileStateItem currentState = currentStates.value(fileName);
+            Internal::FileStateItem expectedState = d->m_states.value(fileName).expected;
+            Internal::FileStateItem lastState = d->m_states.value(fileName).lastUpdatedState.value(file);
+            // did the file actually change?
+            if (lastState.modified == currentState.modified && lastState.permissions == currentState.permissions)
                 continue;
+            changed = true;
+
+            // was it only a permission change?
+            if (lastState.modified == currentState.modified)
+                continue;
+
+            // was the change unexpected?
+            if (currentState.modified != expectedState.modified || currentState.permissions != expectedState.permissions) {
+                behavior = IFile::TriggerExternal;
             }
-            // we've got some modification
-            // check if it's contents or permissions:
-            if (it.value().modified == fi.lastModified()) {
-                // Only permission change
-                file->reload(IFile::FlagReload, IFile::TypePermissions);
-            // now we know it's a content change or file was removed
-            } else if (defaultBehavior == IFile::ReloadUnmodified
-                       && type == IFile::TypeContents && !file->isModified()) {
-                // content change, but unmodified (and settings say to reload in this case)
+
+            // find out the type
+            IFile::ChangeType fileChange = changeTypes.value(fileName);
+            if (fileChange == IFile::TypeRemoved) {
+                type = IFile::TypeRemoved;
+                behavior = IFile::TriggerExternal; // removed files always trigger externally
+            } else if (fileChange == IFile::TypeContents && type == IFile::TypePermissions) {
+                type = IFile::TypeContents;
+            }
+        }
+
+        if (!changed) // probably because the change was blocked with (un)blockFileChange
+            continue;
+
+        // handle it!
+        d->m_blockedIFile = file;
+
+        // we've got some modification
+        // check if it's contents or permissions:
+        if (type == IFile::TypePermissions) {
+            // Only permission change
+            file->reload(IFile::FlagReload, IFile::TypePermissions);
+        // now we know it's a content change or file was removed
+        } else if (defaultBehavior == IFile::ReloadUnmodified
+                   && type == IFile::TypeContents && !file->isModified()) {
+            // content change, but unmodified (and settings say to reload in this case)
+            file->reload(IFile::FlagReload, type);
+        // file was removed or it's a content change and the default behavior for
+        // unmodified files didn't kick in
+        } else if (defaultBehavior == IFile::IgnoreAll) {
+            // content change or removed, but settings say ignore
+            file->reload(IFile::FlagIgnore, type);
+        // either the default behavior is to always ask,
+        // or the ReloadUnmodified default behavior didn't kick in,
+        // so do whatever the IFile wants us to do
+        } else {
+            // check if IFile wants us to ask
+            if (file->reloadBehavior(behavior, type) == IFile::BehaviorSilent) {
+                // content change or removed, IFile wants silent handling
                 file->reload(IFile::FlagReload, type);
-            // file was removed or it's a content change and the default behavior for
-            // unmodified files didn't kick in
-            } else if (defaultBehavior == IFile::IgnoreAll) {
-                // content change or removed, but settings say ignore
-                file->reload(IFile::FlagIgnore, type);
-            // either the default behavior is to always ask,
-            // or the ReloadUnmodified default behavior didn't kick in,
-            // so do whatever the IFile wants us to do
-            } else {
-                // check if IFile wants us to ask
-                if (file->reloadBehavior(behavior, type) == IFile::BehaviorSilent) {
-                    // content change or removed, IFile wants silent handling
-                    file->reload(IFile::FlagReload, type);
-                // IFile wants us to ask
-                } else if (type == IFile::TypeContents) {
-                    // content change, IFile wants to ask user
-                    if (previousAnswer == Utils::ReloadNone) {
-                        // answer already given, ignore
-                        file->reload(IFile::FlagIgnore, IFile::TypeContents);
-                    } else if (previousAnswer == Utils::ReloadAll) {
-                        // answer already given, reload
+            // IFile wants us to ask
+            } else if (type == IFile::TypeContents) {
+                // content change, IFile wants to ask user
+                if (previousAnswer == Utils::ReloadNone) {
+                    // answer already given, ignore
+                    file->reload(IFile::FlagIgnore, IFile::TypeContents);
+                } else if (previousAnswer == Utils::ReloadAll) {
+                    // answer already given, reload
+                    file->reload(IFile::FlagReload, IFile::TypeContents);
+                } else {
+                    // Ask about content change
+                    previousAnswer = Utils::reloadPrompt(file->fileName(), file->isModified(), QApplication::activeWindow());
+                    switch (previousAnswer) {
+                    case Utils::ReloadAll:
+                    case Utils::ReloadCurrent:
                         file->reload(IFile::FlagReload, IFile::TypeContents);
-                    } else {
-                        // Ask about content change
-                        previousAnswer = Utils::reloadPrompt(fileName, file->isModified(), QApplication::activeWindow());
-                        switch (previousAnswer) {
-                        case Utils::ReloadAll:
-                        case Utils::ReloadCurrent:
-                            file->reload(IFile::FlagReload, IFile::TypeContents);
-                            break;
-                        case Utils::ReloadSkipCurrent:
-                        case Utils::ReloadNone:
-                            file->reload(IFile::FlagIgnore, IFile::TypeContents);
-                            break;
-                        }
+                        break;
+                    case Utils::ReloadSkipCurrent:
+                    case Utils::ReloadNone:
+                        file->reload(IFile::FlagIgnore, IFile::TypeContents);
+                        break;
                     }
-                // IFile wants us to ask, and it's the TypeRemoved case
-                } else {
-                    // Ask about removed file
-                    bool unhandled = true;
-                    while (unhandled) {
-                        switch (Utils::fileDeletedPrompt(fileName, QApplication::activeWindow())) {
-                        case Utils::FileDeletedSave:
-                            filesToSave.insert(file, fileName);
-                            unhandled = false;
-                            break;
-                        case Utils::FileDeletedSaveAs:
-                            {
-                                const QString &saveFileName = getSaveAsFileName(file);
-                                if (!saveFileName.isEmpty()) {
-                                    filesToSave.insert(file, saveFileName);
-                                    unhandled = false;
-                                }
-                                break;
-                            }
-                        case Utils::FileDeletedClose:
-                            editorsToClose << EditorManager::instance()->editorsForFile(file);
+                }
+            // IFile wants us to ask, and it's the TypeRemoved case
+            } else {
+                // Ask about removed file
+                bool unhandled = true;
+                while (unhandled) {
+                    switch (Utils::fileDeletedPrompt(file->fileName(), QApplication::activeWindow())) {
+                    case Utils::FileDeletedSave:
+                        filesToSave.insert(file, file->fileName());
+                        unhandled = false;
+                        break;
+                    case Utils::FileDeletedSaveAs:
+                    {
+                        const QString &saveFileName = getSaveAsFileName(file);
+                        if (!saveFileName.isEmpty()) {
+                            filesToSave.insert(file, saveFileName);
                             unhandled = false;
-                            break;
                         }
+                        break;
+                    }
+                    case Utils::FileDeletedClose:
+                        editorsToClose << EditorManager::instance()->editorsForFile(file);
+                        unhandled = false;
+                        break;
                     }
                 }
             }
-
-            updateFileInfo(file);
-            d->m_blockedIFile = 0;
         }
-    }
 
-    // cleanup
-    if (!modifiedFileNames.isEmpty()) {
-        d->m_fileWatcher->removePaths(modifiedFileNames);
-        d->m_fileWatcher->addPaths(modifiedFileNames);
+        // update file info, also handling if e.g. link target has changed
+        removeFileInfo(file);
+        addFileInfo(file);
+        d->m_blockedIFile = 0;
     }
+
+    // clean up
     d->m_changedFiles.clear();
 
     // handle deleted files
@@ -929,6 +993,8 @@ void FileManager::checkForReload()
     }
 
     d->m_blockActivated = false;
+
+//    dump();
 }
 
 void FileManager::syncWithEditor(Core::IContext *context)
@@ -951,10 +1017,10 @@ void FileManager::addToRecentFiles(const QString &fileName)
 {
     if (fileName.isEmpty())
         return;
-    QString unifiedForm(fixFileName(fileName));
+    QString unifiedForm(fixFileName(fileName, KeepLinks));
     QMutableStringListIterator it(d->m_recentFiles);
     while (it.hasNext()) {
-        QString recentUnifiedForm(fixFileName(it.next()));
+        QString recentUnifiedForm(fixFileName(it.next(), KeepLinks));
         if (unifiedForm == recentUnifiedForm)
             it.remove();
     }
diff --git a/src/plugins/coreplugin/filemanager.h b/src/plugins/coreplugin/filemanager.h
index c6e827e3c1ad7a0306b9a166734f504048c1edb1..5db7453282d0d4d4ace7643433b7603536f3589b 100644
--- a/src/plugins/coreplugin/filemanager.h
+++ b/src/plugins/coreplugin/filemanager.h
@@ -53,6 +53,11 @@ class CORE_EXPORT FileManager : public QObject
 {
     Q_OBJECT
 public:
+    enum FixMode {
+        ResolveLinks,
+        KeepLinks
+    };
+
     explicit FileManager(QMainWindow *ew);
     virtual ~FileManager();
 
@@ -80,7 +85,7 @@ public:
     QString currentFile() const;
 
     // helper methods
-    static QString fixFileName(const QString &fileName);
+    static QString fixFileName(const QString &fileName, FixMode fixmode);
 
     QStringList getOpenFileNames(const QString &filters,
                                  const QString path = QString(),
@@ -134,11 +139,9 @@ private:
     void readSettings();
     void dump();
     void addFileInfo(IFile *file);
+    void addFileInfo(const QString &fileName, IFile *file, bool isLink);
     void removeFileInfo(IFile *file);
-    void removeFileInfo(const QString &fileName, IFile *file);
 
-    void addWatch(const QString &filename);
-    void removeWatch(const QString &filename);
     void updateFileInfo(IFile *file);
     void updateExpectedState(const QString &fileName);
 
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index 027e399155705a5ba9923f4b52b93a91018c0ec8..8ac905e9ce59d6f83f690de788f55912e8289adb 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -2262,7 +2262,7 @@ void ProjectExplorerPlugin::renameFile(Node *node, const QString &to)
     FileNode *fileNode = qobject_cast<FileNode *>(node);
     if (!fileNode)
         return;
-    QString orgFilePath = node->path();
+    QString orgFilePath = QFileInfo(node->path()).absoluteFilePath();
     QString dir = QFileInfo(orgFilePath).absolutePath();
     QString newFilePath = dir + "/" + to;
     Core::ICore *core = Core::ICore::instance();