diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h
index 90831b2323f4ec6255e2180b57100dcefd685a8a..ad7b8175f0e9f16a69184918f8a006bfe0da11fb 100644
--- a/src/plugins/coreplugin/iversioncontrol.h
+++ b/src/plugins/coreplugin/iversioncontrol.h
@@ -41,8 +41,11 @@ class CORE_EXPORT IVersionControl : public QObject
 {
     Q_OBJECT
 public:
-    enum Operation { AddOperation, DeleteOperation, OpenOperation,
-                     CreateRepositoryOperation };
+    enum Operation {
+        AddOperation, DeleteOperation, OpenOperation,
+        CreateRepositoryOperation,
+        SnapshotOperations
+        };
 
     explicit IVersionControl(QObject *parent = 0) : QObject(parent) {}
     virtual ~IVersionControl() {}
@@ -98,10 +101,31 @@ public:
     virtual bool vcsDelete(const QString &filename) = 0;
 
     /*!
-     * Called to initialize the version control systemin a directory.
+     * Called to initialize the version control system in a directory.
      */
     virtual bool vcsCreateRepository(const QString &directory) = 0;
 
+    /*!
+     * Create a snapshot of the current state and return an identifier or
+     * an empty string in case of failure.
+     */
+    virtual QString vcsCreateSnapshot(const QString &topLevel) = 0;
+
+    /*!
+     * List snapshots.
+     */
+    virtual QStringList vcsSnapshots(const QString &topLevel) = 0;
+
+    /*!
+     * Restore a snapshot.
+     */
+    virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name) = 0;
+
+    /*!
+     * Remove a snapshot.
+     */
+    virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name) = 0;
+
 signals:
     void repositoryChanged(const QString &repository);
     void filesChanged(const QStringList &files);
diff --git a/src/plugins/cvs/cvscontrol.cpp b/src/plugins/cvs/cvscontrol.cpp
index da57f43d5a96c17e269139935c857d3e6d4751e9..343834cc66da9e95299b37209f210d593baa13a8 100644
--- a/src/plugins/cvs/cvscontrol.cpp
+++ b/src/plugins/cvs/cvscontrol.cpp
@@ -55,6 +55,7 @@ bool CVSControl::supportsOperation(Operation operation) const
         break;
     case OpenOperation:
     case CreateRepositoryOperation:
+    case SnapshotOperations:
         rc = false;
         break;
     }
@@ -84,6 +85,26 @@ bool CVSControl::vcsCreateRepository(const QString &)
     return false;
 }
 
+QString CVSControl::vcsCreateSnapshot(const QString &)
+{
+    return QString();
+}
+
+QStringList CVSControl::vcsSnapshots(const QString &)
+{
+    return QStringList();
+}
+
+bool CVSControl::vcsRestoreSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
+bool CVSControl::vcsRemoveSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
 bool CVSControl::managesDirectory(const QString &directory) const
 {
     return m_plugin->managesDirectory(directory);
diff --git a/src/plugins/cvs/cvscontrol.h b/src/plugins/cvs/cvscontrol.h
index f53c38b92574179797a966d7fba6759a84551a96..2d8a1cc99c7b606def2f28de5b0dca9e524e6a66 100644
--- a/src/plugins/cvs/cvscontrol.h
+++ b/src/plugins/cvs/cvscontrol.h
@@ -53,6 +53,10 @@ public:
     virtual bool vcsAdd(const QString &fileName);
     virtual bool vcsDelete(const QString &filename);
     virtual bool vcsCreateRepository(const QString &directory);
+    virtual QString vcsCreateSnapshot(const QString &topLevel);
+    virtual QStringList vcsSnapshots(const QString &topLevel);
+    virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
+    virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
 
     void emitRepositoryChanged(const QString &s);
     void emitFilesChanged(const QStringList &l);
diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro
index fa87f66d3d80c5003fd6f7cb808aeebb4acccaea..0ef64c74bb39ce2c71dffaa7910f007212c92afa 100644
--- a/src/plugins/git/git.pro
+++ b/src/plugins/git/git.pro
@@ -22,7 +22,9 @@ HEADERS += gitplugin.h \
     branchmodel.h \
     gitcommand.h \
     clonewizard.h \
-    clonewizardpage.h
+    clonewizardpage.h \
+    stashdialog.h \
+    gitutils.h
 SOURCES += gitplugin.cpp \
     gitclient.cpp \
     changeselectiondialog.cpp \
@@ -38,11 +40,13 @@ SOURCES += gitplugin.cpp \
     branchmodel.cpp \
     gitcommand.cpp \
     clonewizard.cpp \
-    clonewizardpage.cpp
+    clonewizardpage.cpp \
+    stashdialog.cpp \
+    gitutils.cpp
 FORMS += changeselectiondialog.ui \
     settingspage.ui \
     gitsubmitpanel.ui \
-    branchdialog.ui
-
+    branchdialog.ui \
+    stashdialog.ui
 OTHER_FILES += ScmGit.pluginspec
 include(gitorious/gitorious.pri)
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 6e45bc7818c13e42fab66c6bb24716a7b2538878..88b88b9c633891fba265d9b45cd42a58c9fd4c26 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -29,6 +29,7 @@
 
 #include "gitclient.h"
 #include "gitcommand.h"
+#include "gitutils.h"
 
 #include "commitdata.h"
 #include "gitconstants.h"
@@ -64,15 +65,12 @@
 #include <QtGui/QMessageBox>
 #include <QtGui/QPushButton>
 
-using namespace Git;
-using namespace Git::Internal;
-
 static const char *const kGitDirectoryC = ".git";
 static const char *const kBranchIndicatorC = "# On branch";
 
 static inline QString msgServerFailure()
 {
-    return GitClient::tr(
+    return Git::Internal::GitClient::tr(
 "Note that the git plugin for QtCreator is not able to interact with the server "
 "so far. Thus, manual ssh-identification etc. will not work.");
 }
@@ -85,6 +83,29 @@ inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property
     return 0;
 }
 
+// Return converted command output, remove '\r' read on Windows
+static inline QString commandOutputFromLocal8Bit(const QByteArray &a)
+{
+    QString output = QString::fromLocal8Bit(a);
+    output.remove(QLatin1Char('\r'));
+    return output;
+}
+
+// Return converted command output split into lines
+static inline QStringList commandOutputLinesFromLocal8Bit(const QByteArray &a)
+{
+    QString output = commandOutputFromLocal8Bit(a);
+    const QChar newLine = QLatin1Char('\n');
+    if (output.endsWith(newLine))
+        output.truncate(output.size() - 1);
+    if (output.isEmpty())
+        return QStringList();
+    return output.split(newLine);
+}
+
+namespace Git {
+namespace Internal {
+
 static inline QString msgRepositoryNotFound(const QString &dir)
 {
     return GitClient::tr("Unable to determine the repository for %1.").arg(dir);
@@ -103,6 +124,9 @@ static QString formatCommand(const QString &binary, const QStringList &args)
 }
 
 // ---------------- GitClient
+
+const char *GitClient::stashNamePrefix = "stash@{";
+
 GitClient::GitClient(GitPlugin* plugin)
   : m_msgWait(tr("Waiting for data...")),
     m_plugin(plugin),
@@ -364,6 +388,30 @@ void GitClient::checkoutBranch(const QString &workingDirectory, const QString &b
     connectRepositoryChanged(workingDirectory, cmd);
 }
 
+bool GitClient::synchronousCheckoutBranch(const QString &workingDirectory,
+                                    const QString &branch,
+                                    QString *errorMessage /* = 0 */)
+{
+    QByteArray outputText;
+    QByteArray errorText;
+    QStringList arguments;
+    arguments << QLatin1String("checkout") << branch;
+    const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
+    const QString output = commandOutputFromLocal8Bit(outputText);
+    VCSBase::VCSBaseOutputWindow::instance()->append(output);
+    if (!rc) {
+        const QString stdErr = commandOutputFromLocal8Bit(errorText);
+        const QString msg = tr("Unable to checkout %1 of %2: %3").arg(branch, workingDirectory, stdErr);
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
+        }
+        return false;
+    }
+    return true;
+}
+
 void GitClient::checkout(const QString &workingDirectory, const QString &fileName)
 {
     // Passing an empty argument as the file name is very dangereous, since this makes
@@ -408,22 +456,12 @@ bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringLis
     const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
     if (!rc) {
         const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()).
-                                     arg(workingDirectory, QString::fromLocal8Bit(errorText));
+                                     arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
         VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
     }
     return rc;
 }
 
-bool GitClient::synchronousReset(const QString &workingDirectory,
-                                 const QStringList &files)
-{
-    QString errorMessage;
-    const bool rc = synchronousReset(workingDirectory, files, &errorMessage);
-    if (!rc)
-        VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
-    return rc;
-}
-
 bool GitClient::synchronousReset(const QString &workingDirectory,
                                  const QStringList &files,
                                  QString *errorMessage)
@@ -433,14 +471,27 @@ bool GitClient::synchronousReset(const QString &workingDirectory,
     QByteArray outputText;
     QByteArray errorText;
     QStringList arguments;
-    arguments << QLatin1String("reset") << QLatin1String("HEAD") << QLatin1String("--") << files;
+    arguments << QLatin1String("reset");
+    if (files.isEmpty()) {
+        arguments << QLatin1String("--hard");
+    } else {
+        arguments << QLatin1String("HEAD") << QLatin1String("--") << files;
+    }
     const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
-    const QString output = QString::fromLocal8Bit(outputText);
+    const QString output = commandOutputFromLocal8Bit(outputText);
     VCSBase::VCSBaseOutputWindow::instance()->append(output);
     // Note that git exits with 1 even if the operation is successful
     // Assume real failure if the output does not contain "foo.cpp modified"
     if (!rc && !output.contains(QLatin1String("modified"))) {
-        *errorMessage = tr("Unable to reset %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, QString::fromLocal8Bit(errorText));
+        const QString stdErr = commandOutputFromLocal8Bit(errorText);
+        const QString msg = files.isEmpty() ?
+                            tr("Unable to reset %1: %2").arg(workingDirectory, stdErr) :
+                            tr("Unable to reset %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, stdErr);
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
+        }
         return false;
     }
     return true;
@@ -456,25 +507,41 @@ bool GitClient::synchronousInit(const QString &workingDirectory)
     const QStringList arguments(QLatin1String("init"));
     const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
     // '[Re]Initialized...'
-    VCSBase::VCSBaseOutputWindow::instance()->append(QString::fromLocal8Bit(outputText));
+    VCSBase::VCSBaseOutputWindow::instance()->append(commandOutputFromLocal8Bit(outputText));
     if (!rc)
-        VCSBase::VCSBaseOutputWindow::instance()->append(QString::fromLocal8Bit(errorText));
+        VCSBase::VCSBaseOutputWindow::instance()->appendError(commandOutputFromLocal8Bit(errorText));
     return rc;
 }
 
-bool GitClient::synchronousCheckout(const QString &workingDirectory,
-                                    const QStringList &files,
-                                    QString *errorMessage)
+/* Checkout, supports:
+ * git checkout -- <files>
+ * git checkout revision -- <files>
+ * git checkout revision -- . */
+bool GitClient::synchronousCheckoutFiles(const QString &workingDirectory,
+                                         QStringList files /* = QStringList() */,
+                                         QString revision /* = QString() */,
+                                         QString *errorMessage /* = 0 */)
 {
     if (Git::Constants::debug)
         qDebug() << Q_FUNC_INFO << workingDirectory << files;
+    if (revision.isEmpty())
+        revision = QLatin1String("HEAD");
+    if (files.isEmpty())
+        files = QStringList(QString(QLatin1Char('.')));
     QByteArray outputText;
     QByteArray errorText;
     QStringList arguments;
-    arguments << QLatin1String("checkout") << QLatin1String("--") << files;
+    arguments << QLatin1String("checkout") << revision << QLatin1String("--") << files;
     const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
     if (!rc) {
-        *errorMessage = tr("Unable to checkout %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, QString::fromLocal8Bit(errorText));
+        const QString fileArg = files.join(QLatin1String(", "));
+        const QString msg = tr("Unable to checkout %1 of %2 in %3: %4").
+                            arg(revision, fileArg, workingDirectory, commandOutputFromLocal8Bit(errorText));
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
+        }
         return false;
     }
     return true;
@@ -533,13 +600,12 @@ bool GitClient::synchronousParentRevisions(const QString &workingDirectory,
     }
     const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
     if (!rc) {
-        *errorMessage = msgParentRevisionFailed(workingDirectory, revision, QString::fromLocal8Bit(errorText));
+        *errorMessage = msgParentRevisionFailed(workingDirectory, revision, commandOutputFromLocal8Bit(errorText));
         return false;
     }
     // Should result in one line of blank-delimited revisions, specifying current first
     // unless it is top.
-    QString outputText = QString::fromLocal8Bit(outputTextData);
-    outputText.remove(QLatin1Char('\r'));
+    QString outputText = commandOutputFromLocal8Bit(outputTextData);
     outputText.remove(QLatin1Char('\n'));
     if (!splitCommitParents(outputText, 0, parents)) {
         *errorMessage = msgParentRevisionFailed(workingDirectory, revision, msgInvalidRevision());
@@ -578,6 +644,75 @@ bool GitClient::synchronousShortDescriptions(const QString &workingDirectory, co
     return true;
 }
 
+static inline QString msgCannotDetermineBranch(const QString &workingDirectory, const QString &why)
+{
+    return GitClient::tr("Unable to retrieve branch of %1: %2").arg(workingDirectory, why);
+}
+
+// Retrieve head revision/branch
+bool GitClient::synchronousTopRevision(const QString &workingDirectory,
+                                       QString *revision /* = 0 */,
+                                       QString *branch /* = 0 */,
+                                       QString *errorMessageIn /* = 0 */)
+{
+    if (Git::Constants::debug)
+        qDebug() << Q_FUNC_INFO << workingDirectory;
+    QByteArray outputTextData;
+    QByteArray errorText;
+    QStringList arguments;
+    QString errorMessage;
+    do {
+        // get revision
+        if (revision) {
+            revision->clear();
+            arguments << QLatin1String("log") << QLatin1String(noColorOption)
+                    <<  QLatin1String("--max-count=1") << QLatin1String("--pretty=format:%H");
+            if (!synchronousGit(workingDirectory, arguments, &outputTextData, &errorText)) {
+                errorMessage =  tr("Unable to retrieve top revision of %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
+                break;
+            }
+            *revision = commandOutputFromLocal8Bit(outputTextData);
+            revision->remove(QLatin1Char('\n'));
+        } // revision desired
+        // get branch
+        if (branch) {
+            branch->clear();
+            arguments.clear();
+            arguments << QLatin1String("branch") << QLatin1String(noColorOption);
+            if (!synchronousGit(workingDirectory, arguments, &outputTextData, &errorText)) {
+                errorMessage = msgCannotDetermineBranch(workingDirectory, commandOutputFromLocal8Bit(errorText));
+                break;
+            }
+            /* parse output for current branch: \code
+* master
+  branch2
+\endcode */
+            const QString branchPrefix = QLatin1String("* ");
+            foreach(const QString &line, commandOutputLinesFromLocal8Bit(outputTextData)) {
+                if (line.startsWith(branchPrefix)) {
+                    *branch = line;
+                    branch->remove(0, branchPrefix.size());
+                    break;
+                }
+            }
+            if (branch->isEmpty()) {
+                errorMessage = msgCannotDetermineBranch(workingDirectory,
+                                                        QString::fromLatin1("Internal error: Failed to parse output: %1").arg(commandOutputFromLocal8Bit(outputTextData)));
+                break;
+            }
+        } // branch
+    } while (false);
+    const bool failed = (revision && revision->isEmpty()) || (branch && branch->isEmpty());
+    if (failed && !errorMessage.isEmpty()) {
+        if (errorMessageIn) {
+            *errorMessageIn = errorMessage;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
+        }
+    }
+    return !failed;
+}
+
 // Format an entry in a one-liner for selection list using git log.
 bool GitClient::synchronousShortDescription(const QString &workingDirectory,
                                     const QString &revision,
@@ -595,17 +730,83 @@ bool GitClient::synchronousShortDescription(const QString &workingDirectory,
               << QLatin1String("--max-count=1") << revision;
     const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
     if (!rc) {
-        *errorMessage = tr("Unable to describe revision %1 in %2: %3").arg(revision, workingDirectory, QString::fromLocal8Bit(errorText));
+        *errorMessage = tr("Unable to describe revision %1 in %2: %3").arg(revision, workingDirectory, commandOutputFromLocal8Bit(errorText));
         return false;
     }
-    *description = QString::fromLocal8Bit(outputTextData);
-    description->remove(QLatin1Char('\r'));
+    *description = commandOutputFromLocal8Bit(outputTextData);
     if (description->endsWith(QLatin1Char('\n')))
         description->truncate(description->size() - 1);
     return true;
 }
 
-bool GitClient::synchronousStash(const QString &workingDirectory, QString *errorMessage)
+// Create a default message to be used for describing stashes
+static inline QString creatorStashMessage(const QString &keyword = QString())
+{
+    QString rc = QCoreApplication::applicationName();
+    rc += QLatin1Char(' ');
+    if (!keyword.isEmpty()) {
+        rc += keyword;
+        rc += QLatin1Char(' ');
+    }
+    rc += QDateTime::currentDateTime().toString(Qt::ISODate);
+    return rc;
+}
+
+/* Do a stash and return the message as identifier. Note that stash names (stash{n})
+ * shift as they are pushed, so, enforce the use of messages to identify them. Flags:
+ * StashPromptDescription: Prompt the user for a description message.
+ * StashImmediateRestore: Immediately re-apply this stash (used for snapshots), user keeps on working
+ * StashIgnoreUnchanged: Be quiet about unchanged repositories (used for IVersionControl's snapshots). */
+
+QString GitClient::synchronousStash(const QString &workingDirectory,
+                                    const QString &messageKeyword /*  = QString() */,
+                                    unsigned flags,
+                                    bool *unchanged /* =0 */)
+{
+    if (unchanged)
+        *unchanged = false;
+    QString message;
+    bool success = false;
+    // Check for changes and stash
+    QString errorMessage;
+    switch (gitStatus(workingDirectory, false, 0, &errorMessage)) {
+    case  StatusChanged: {
+            message = creatorStashMessage(messageKeyword);
+            do {
+                if ((flags & StashPromptDescription)) {
+                    if (!inputText(Core::ICore::instance()->mainWindow(),
+                         tr("Stash description"), tr("Description:"), &message))
+                        break;
+                }
+                if (!executeSynchronousStash(workingDirectory, message))
+                    break;
+                if ((flags & StashImmediateRestore)
+                    && !synchronousStashRestore(workingDirectory, QLatin1String("stash@{0}")))
+                    break;
+                success = true;
+            } while (false);
+        }
+        break;
+    case StatusUnchanged:
+        if (unchanged)
+            *unchanged = true;
+        if (!(flags & StashIgnoreUnchanged))
+            VCSBase::VCSBaseOutputWindow::instance()->append(msgNoChangedFiles());
+        break;
+    case StatusFailed:
+        VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
+        break;
+    }
+    if (!success)
+        message.clear();
+    if (Git::Constants::debug)
+        qDebug() << Q_FUNC_INFO << '\n' << workingDirectory << messageKeyword << "returns" << message;
+    return message;
+}
+
+bool GitClient::executeSynchronousStash(const QString &workingDirectory,
+                                 const QString &message,
+                                 QString *errorMessage /* = 0*/)
 {
     if (Git::Constants::debug)
         qDebug() << Q_FUNC_INFO << workingDirectory;
@@ -613,14 +814,50 @@ bool GitClient::synchronousStash(const QString &workingDirectory, QString *error
     QByteArray errorText;
     QStringList arguments;
     arguments << QLatin1String("stash");
+    if (!message.isEmpty())
+        arguments << QLatin1String("save") << message;
     const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
     if (!rc) {
-        *errorMessage = tr("Unable stash in %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
+        const QString msg = tr("Unable stash in %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->append(msg);
+        }
         return false;
     }
     return true;
 }
 
+// Resolve a stash name from message
+bool GitClient::stashNameFromMessage(const QString &workingDirectory,
+                                     const QString &message, QString *name,
+                                     QString *errorMessage /* = 0 */)
+{
+    // All happy
+    if (message.startsWith(QLatin1String(stashNamePrefix))) {
+        *name = message;
+        return true;
+    }
+    // Retrieve list and find via message
+    QList<Stash> stashes;
+    if (!synchronousStashList(workingDirectory, &stashes, errorMessage))
+        return false;
+    foreach (const Stash &s, stashes) {
+        if (s.message == message) {
+            *name = s.name;
+            return true;
+        }
+    }
+    const QString msg = tr("Unable to resolve stash message '%1' in %2").arg(message, workingDirectory);
+    if (errorMessage) {
+        *errorMessage = msg;
+    } else {
+        VCSBase::VCSBaseOutputWindow::instance()->append(msg);
+    }
+    return  false;
+}
+
 bool GitClient::synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
                                      QString *output, QString *errorMessage)
 {
@@ -631,10 +868,10 @@ bool GitClient::synchronousBranchCmd(const QString &workingDirectory, QStringLis
     QByteArray errorText;
     const bool rc = synchronousGit(workingDirectory, branchArgs, &outputText, &errorText);
     if (!rc) {
-        *errorMessage = tr("Unable to run branch command: %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
+        *errorMessage = tr("Unable to run branch command: %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
         return false;
     }
-    *output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
+    *output = commandOutputFromLocal8Bit(outputText);
     return true;
 }
 
@@ -649,10 +886,10 @@ bool GitClient::synchronousShow(const QString &workingDirectory, const QString &
     QByteArray errorText;
     const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText);
     if (!rc) {
-        *errorMessage = tr("Unable to run show: %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
+        *errorMessage = tr("Unable to run show: %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
         return false;
     }
-    *output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
+    *output = commandOutputFromLocal8Bit(outputText);
     return true;
 }
 
@@ -805,7 +1042,7 @@ GitClient::StashResult GitClient::ensureStash(const QString &workingDirectory, Q
         case QMessageBox::Cancel:
             return StashCanceled;
         case QMessageBox::Yes:
-            if (!synchronousStash(workingDirectory, errorMessage))
+            if (!executeSynchronousStash(workingDirectory, creatorStashMessage(QLatin1String("push")), errorMessage))
                 return StashFailed;
             break;
         case QMessageBox::No: // At your own risk, so.
@@ -845,11 +1082,11 @@ GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory,
     const bool statusRc = synchronousGit(workingDirectory, statusArgs, &outputText, &errorText);
     GitCommand::removeColorCodes(&outputText);
     if (output)
-        *output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
+        *output = commandOutputFromLocal8Bit(outputText);
     // Is it something really fatal?
     if (!statusRc && !outputText.contains(kBranchIndicatorC)) {
         if (errorMessage) {
-            const QString error = QString::fromLocal8Bit(errorText).remove(QLatin1Char('\r'));
+            const QString error = commandOutputFromLocal8Bit(errorText);
             *errorMessage = tr("Unable to obtain the status: %1").arg(error);
         }
         return StatusFailed;
@@ -890,7 +1127,7 @@ bool GitClient::getCommitData(const QString &workingDirectory,
     if (QFileInfo(descriptionFile).isFile()) {
         QFile file(descriptionFile);
         if (file.open(QIODevice::ReadOnly|QIODevice::Text))
-            d->panelInfo.description = QString::fromLocal8Bit(file.readAll()).trimmed();
+            d->panelInfo.description = commandOutputFromLocal8Bit(file.readAll()).trimmed();
     }
 
     // Run status. Note that it has exitcode 1 if there are no added files.
@@ -996,7 +1233,7 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory,
     if (rc) {
         VCSBase::VCSBaseOutputWindow::instance()->append(tr("Committed %n file(s).\n", 0, checkedFiles.size()));
     } else {
-        VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(QString::fromLocal8Bit(errorText)));
+        VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(commandOutputFromLocal8Bit(errorText)));
     }
     return rc;
 }
@@ -1086,7 +1323,7 @@ GitClient::RevertResult GitClient::revertI(QStringList files, bool *ptrToIsDirec
     if (!stagedFiles.empty() && !synchronousReset(repoDirectory, stagedFiles, errorMessage))
         return RevertFailed;
     // Finally revert!
-    if (!synchronousCheckout(repoDirectory, stagedFiles + unstagedFiles, errorMessage))
+    if (!synchronousCheckoutFiles(repoDirectory, stagedFiles + unstagedFiles, QString(), errorMessage))
         return RevertFailed;
     return RevertOk;
 }
@@ -1128,23 +1365,6 @@ QString GitClient::msgNoChangedFiles()
     return tr("There are no modified files.");
 }
 
-void GitClient::stash(const QString &workingDirectory)
-{
-    // Check for changes and stash
-    QString errorMessage;
-    switch (gitStatus(workingDirectory, false, 0, &errorMessage)) {
-    case  StatusChanged:
-        executeGit(workingDirectory, QStringList(QLatin1String("stash")), 0, true);
-        break;
-    case StatusUnchanged:
-        VCSBase::VCSBaseOutputWindow::instance()->append(msgNoChangedFiles());
-        break;
-    case StatusFailed:
-        VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
-        break;
-    }
-}
-
 void GitClient::stashPop(const QString &workingDirectory)
 {
     QStringList arguments(QLatin1String("stash"));
@@ -1153,6 +1373,70 @@ void GitClient::stashPop(const QString &workingDirectory)
     connectRepositoryChanged(workingDirectory, cmd);
 }
 
+bool GitClient::synchronousStashRestore(const QString &workingDirectory,
+                                        const QString &stash,
+                                        const QString &branch /* = QString()*/,
+                                        QString *errorMessage)
+{
+    QStringList arguments(QLatin1String("stash"));
+    if (branch.isEmpty()) {
+        arguments << QLatin1String("apply") << stash;
+    } else {
+        arguments << QLatin1String("branch") << branch << stash;
+    }
+    QByteArray outputText;
+    QByteArray errorText;
+    const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
+    if (!rc) {
+        const QString stdErr = commandOutputFromLocal8Bit(errorText);
+        const QString msg = branch.isEmpty() ?
+                            tr("Unable to restore stash %1: %2").arg(workingDirectory, stdErr) :
+                            tr("Unable to restore stash %1 to branch %2: %3").arg(workingDirectory, branch, stdErr);
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->append(msg);
+        }
+        return false;
+    }
+    QString output = commandOutputFromLocal8Bit(outputText);
+    if (!output.isEmpty())
+        VCSBase::VCSBaseOutputWindow::instance()->append(output);
+    GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
+    return true;
+}
+
+bool GitClient::synchronousStashRemove(const QString &workingDirectory,
+                            const QString &stash /* = QString() */,
+                            QString *errorMessage /* = 0 */)
+{
+    QStringList arguments(QLatin1String("stash"));
+    if (stash.isEmpty()) {
+        arguments << QLatin1String("clear");
+    } else {
+        arguments << QLatin1String("drop") << stash;
+    }
+    QByteArray outputText;
+    QByteArray errorText;
+    const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
+    if (!rc) {
+        const QString stdErr = commandOutputFromLocal8Bit(errorText);
+        const QString msg = stash.isEmpty() ?
+                            tr("Unable to remove stashes of %1: %2").arg(workingDirectory, stdErr) :
+                            tr("Unable to remove stash %1 of %2: %3").arg(stash, workingDirectory, stdErr);
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->append(msg);
+        }
+        return false;
+    }
+    QString output = commandOutputFromLocal8Bit(outputText);
+    if (!output.isEmpty())
+        VCSBase::VCSBaseOutputWindow::instance()->append(output);
+    return true;
+}
+
 void GitClient::branchList(const QString &workingDirectory)
 {
     QStringList arguments(QLatin1String("branch"));
@@ -1167,6 +1451,34 @@ void GitClient::stashList(const QString &workingDirectory)
     executeGit(workingDirectory, arguments, 0, true);
 }
 
+bool GitClient::synchronousStashList(const QString &workingDirectory,
+                                     QList<Stash> *stashes,
+                                     QString *errorMessage /* = 0 */)
+{
+    stashes->clear();
+    QStringList arguments(QLatin1String("stash"));
+    arguments << QLatin1String("list") << QLatin1String(noColorOption);
+    QByteArray outputText;
+    QByteArray errorText;
+    const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
+    if (!rc) {
+        const QString msg = tr("Unable retrieve stash list of %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
+        if (errorMessage) {
+            *errorMessage = msg;
+        } else {
+            VCSBase::VCSBaseOutputWindow::instance()->append(msg);
+        }
+        return false;
+    }
+    Stash stash;
+    foreach(const QString &line, commandOutputLinesFromLocal8Bit(outputText))
+        if (stash.parseStashLine(line))
+            stashes->push_back(stash);
+    if (Git::Constants::debug)
+        qDebug() << Q_FUNC_INFO << *stashes;
+    return true;
+}
+
 QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar)
 {
     QStringList arguments;
@@ -1174,7 +1486,7 @@ QString GitClient::readConfig(const QString &workingDirectory, const QStringList
 
     QByteArray outputText;
     if (synchronousGit(workingDirectory, arguments, &outputText, 0, false))
-        return QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
+        return commandOutputFromLocal8Bit(outputText);
     return QString();
 }
 
@@ -1211,3 +1523,6 @@ void GitClient::connectRepositoryChanged(const QString & repository, GitCommand
     connect(cmd, SIGNAL(success()), m_repositoryChangedSignalMapper, SLOT(map()),
             Qt::QueuedConnection);
 }
+
+}
+}
diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h
index 6d6e0c253736e38ac57d95a066ec90f65bcc39df..790273aa06a5c4bfc05330e39787a1af758f58d4 100644
--- a/src/plugins/git/gitclient.h
+++ b/src/plugins/git/gitclient.h
@@ -41,6 +41,7 @@
 QT_BEGIN_NAMESPACE
 class QErrorMessage;
 class QSignalMapper;
+class QDebug;
 QT_END_NAMESPACE
 
 namespace Core {
@@ -59,12 +60,15 @@ class GitOutputWindow;
 class GitCommand;
 struct CommitData;
 struct GitSubmitEditorPanelData;
+struct Stash;
 
 class GitClient : public QObject
 {
     Q_OBJECT
 
 public:
+    static const char *stashNamePrefix;
+
     explicit GitClient(GitPlugin *plugin);
     ~GitClient();
 
@@ -84,17 +88,37 @@ public:
              bool enableAnnotationContextMenu = false);
     void blame(const QString &workingDirectory, const QString &fileName,
                const QString &revision = QString(), int lineNumber = -1);
-    void showCommit(const QString &workingDirectory, const QString &commit);
     void checkout(const QString &workingDirectory, const QString &file);
     void checkoutBranch(const QString &workingDirectory, const QString &branch);
-    void hardReset(const QString &workingDirectory, const QString &commit);
+    void hardReset(const QString &workingDirectory, const QString &commit = QString());
     void addFile(const QString &workingDirectory, const QString &fileName);
     bool synchronousAdd(const QString &workingDirectory, const QStringList &files);
-    bool synchronousReset(const QString &workingDirectory, const QStringList &files);
-    bool synchronousReset(const QString &workingDirectory, const QStringList &files, QString *errorMessage);
+    bool synchronousReset(const QString &workingDirectory,
+                          const QStringList &files = QStringList(),
+                          QString *errorMessage = 0);
     bool synchronousInit(const QString &workingDirectory);
-    bool synchronousCheckout(const QString &workingDirectory, const QStringList &files, QString *errorMessage);
-    bool synchronousStash(const QString &workingDirectory, QString *errorMessage);
+    bool synchronousCheckoutFiles(const QString &workingDirectory,
+                                  QStringList files = QStringList(),
+                                  QString revision = QString(), QString *errorMessage = 0);
+    // Checkout branch
+    bool synchronousCheckoutBranch(const QString &workingDirectory, const QString &branch, QString *errorMessage = 0);
+
+    // Do a stash and return identier.
+    enum { StashPromptDescription = 0x1, StashImmediateRestore = 0x2, StashIgnoreUnchanged = 0x4 };
+    QString synchronousStash(const QString &workingDirectory,
+                             const QString &messageKeyword = QString(),
+                             unsigned flags = 0, bool *unchanged = 0);
+
+    bool executeSynchronousStash(const QString &workingDirectory,
+                                 const QString &message = QString(),
+                                 QString *errorMessage = 0);
+    bool synchronousStashRestore(const QString &workingDirectory,
+                                 const QString &stash,
+                                 const QString &branch = QString(),
+                                 QString *errorMessage = 0);
+    bool synchronousStashRemove(const QString &workingDirectory,
+                                const QString &stash = QString(),
+                                QString *errorMessage = 0);
     bool synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
                               QString *output, QString *errorMessage);
     bool synchronousShow(const QString &workingDirectory, const QString &id,
@@ -110,15 +134,23 @@ public:
                                      const QString &format, QString *description, QString *errorMessage);
     bool synchronousShortDescriptions(const QString &workingDirectory, const QStringList &revisions,
                                       QStringList *descriptions, QString *errorMessage);
+    bool synchronousTopRevision(const QString &workingDirectory, QString *revision = 0,
+                                QString *branch = 0, QString *errorMessage = 0);
 
     void pull(const QString &workingDirectory);
     void push(const QString &workingDirectory);
 
-    void stash(const QString &workingDirectory);
     void stashPop(const QString &workingDirectory);
     void revert(const QStringList &files);
     void branchList(const QString &workingDirectory);
     void stashList(const QString &workingDirectory);
+    bool synchronousStashList(const QString &workingDirectory,
+                              QList<Stash> *stashes,
+                              QString *errorMessage = 0);
+    // Resolve a stash name from message (for IVersionControl's names).
+    bool stashNameFromMessage(const QString &workingDirectory,
+                              const QString &messge, QString *name,
+                              QString *errorMessage = 0);
 
     QString readConfig(const QString &workingDirectory, const QStringList &configVar);
 
diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp
index a25b5f93c1ea2dc7d412f5c57c71e50145e715cf..cb034ff2a53c227f1748b7206dc89128991f9fd1 100644
--- a/src/plugins/git/gitplugin.cpp
+++ b/src/plugins/git/gitplugin.cpp
@@ -39,6 +39,7 @@
 #include "branchdialog.h"
 #include "clonewizard.h"
 #include "gitoriousclonewizard.h"
+#include "stashdialog.h"
 
 #include <coreplugin/icore.h>
 #include <coreplugin/coreconstants.h>
@@ -134,6 +135,7 @@ GitPlugin::GitPlugin() :
     m_undoAction(0),
     m_redoAction(0),
     m_stashAction(0),
+    m_stashSnapshotAction(0),
     m_stashPopAction(0),
     m_stashListAction(0),
     m_branchListAction(0),
@@ -314,36 +316,37 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
 
     gitContainer->addAction(createSeparator(actionManager, globalcontext, QLatin1String("Git.Sep.Global"), this));
 
+    m_stashSnapshotAction = new QAction(tr("Stash snapshot..."), this);
+    m_stashSnapshotAction->setToolTip(tr("Saves the current state of your work."));
+    command = actionManager->registerAction(m_stashSnapshotAction, "Git.StashSnapshot", globalcontext);
+    connect(m_stashSnapshotAction, SIGNAL(triggered()), this, SLOT(stashSnapshot()));
+    gitContainer->addAction(command);
+
     m_stashAction = new QAction(tr("Stash"), this);
-    m_stashAction->setToolTip(tr("Saves the current state of your work."));
+    m_stashAction->setToolTip(tr("Saves the current state of your work and resets the repository."));
     command = actionManager->registerAction(m_stashAction, "Git.Stash", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_stashAction, SIGNAL(triggered()), this, SLOT(stash()));
     gitContainer->addAction(command);
 
     m_pullAction = new QAction(tr("Pull"), this);
     command = actionManager->registerAction(m_pullAction, "Git.Pull", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_pullAction, SIGNAL(triggered()), this, SLOT(pull()));
     gitContainer->addAction(command);
 
     m_stashPopAction = new QAction(tr("Stash Pop"), this);
     m_stashAction->setToolTip(tr("Restores changes saved to the stash list using \"Stash\"."));
     command = actionManager->registerAction(m_stashPopAction, "Git.StashPop", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_stashPopAction, SIGNAL(triggered()), this, SLOT(stashPop()));
     gitContainer->addAction(command);
 
     m_commitAction = new QAction(tr("Commit..."), this);
     command = actionManager->registerAction(m_commitAction, "Git.Commit", globalcontext);
     command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C")));
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_commitAction, SIGNAL(triggered()), this, SLOT(startCommit()));
     gitContainer->addAction(command);
 
     m_pushAction = new QAction(tr("Push"), this);
     command = actionManager->registerAction(m_pushAction, "Git.Push", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_pushAction, SIGNAL(triggered()), this, SLOT(push()));
     gitContainer->addAction(command);
 
@@ -351,22 +354,30 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
 
     m_branchListAction = new QAction(tr("Branches..."), this);
     command = actionManager->registerAction(m_branchListAction, "Git.BranchList", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_branchListAction, SIGNAL(triggered()), this, SLOT(branchList()));
     gitContainer->addAction(command);
 
-    m_stashListAction = new QAction(tr("List Stashes"), this);
+    m_stashListAction = new QAction(tr("Stashes..."), this);
     command = actionManager->registerAction(m_stashListAction, "Git.StashList", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_stashListAction, SIGNAL(triggered()), this, SLOT(stashList()));
     gitContainer->addAction(command);
 
     m_showAction = new QAction(tr("Show Commit..."), this);
     command = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext);
-    command->setAttribute(Core::Command::CA_UpdateText);
     connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
     gitContainer->addAction(command);
 
+    if (0) {
+        const QList<QAction*> snapShotActions = createSnapShotTestActions();
+        const int count = snapShotActions.size();
+        for (int i = 0; i < count; i++) {
+            command = actionManager->registerAction(snapShotActions.at(i),
+                                                    QLatin1String("Git.Snapshot.") + QString::number(i),
+                                                    globalcontext);
+            gitContainer->addAction(command);
+        }
+    }
+
     // Submit editor
     QList<int> submitContext;
     submitContext.push_back(m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Constants::C_GITSUBMITEDITOR)));
@@ -648,9 +659,22 @@ void GitPlugin::push()
 
 void GitPlugin::stash()
 {
+    // Simple stash without prompt, reset repo.
+    const VCSBase::VCSBasePluginState state = currentState();
+    QTC_ASSERT(state.hasTopLevel(), return)
+    const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), 0);
+    if (!id.isEmpty() && m_stashDialog)
+        m_stashDialog->refresh(state.topLevel(), true);
+}
+
+void GitPlugin::stashSnapshot()
+{
+    // Prompt for description, restore immediately and keep on working.
     const VCSBase::VCSBasePluginState state = currentState();
     QTC_ASSERT(state.hasTopLevel(), return)
-    m_gitClient->stash(state.topLevel());
+    const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), GitClient::StashImmediateRestore|GitClient::StashPromptDescription);
+    if (!id.isEmpty() && m_stashDialog)
+        m_stashDialog->refresh(state.topLevel(), true);
 }
 
 void GitPlugin::stashPop()
@@ -676,9 +700,15 @@ void GitPlugin::branchList()
 
 void GitPlugin::stashList()
 {
-    const VCSBase::VCSBasePluginState state = currentState();
-    QTC_ASSERT(state.hasTopLevel(), return)
-    m_gitClient->stashList(state.topLevel());
+    // Raise non-modal stash dialog.
+    if (m_stashDialog) {
+        m_stashDialog->show();
+        m_stashDialog->raise();
+    } else {
+        m_stashDialog = new StashDialog(Core::ICore::instance()->mainWindow());
+        m_stashDialog->refresh(currentState().topLevel(), true);
+        m_stashDialog->show();
+    }
 }
 
 void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
@@ -716,6 +746,7 @@ void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
     m_branchListAction->setEnabled(repositoryEnabled);
     m_stashListAction->setEnabled(repositoryEnabled);
     m_stashAction->setEnabled(repositoryEnabled);
+    m_stashSnapshotAction->setEnabled(repositoryEnabled);
     m_pullAction->setEnabled(repositoryEnabled);
     m_commitAction->setEnabled(repositoryEnabled);
     m_stashPopAction->setEnabled(repositoryEnabled);
@@ -723,6 +754,9 @@ void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
     m_undoRepositoryAction->setEnabled(repositoryEnabled);
     m_pushAction->setEnabled(repositoryEnabled);
 
+    if (m_stashDialog)
+        m_stashDialog->refresh(currentState().topLevel(), false);
+
     // Prompts for repo.
     m_showAction->setEnabled(true);
 }
diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h
index 415efef5c3d28aaacef6f0593588c67d49810bc8..30992c32d2a891e8650e68c88b154320f8929a01 100644
--- a/src/plugins/git/gitplugin.h
+++ b/src/plugins/git/gitplugin.h
@@ -39,6 +39,7 @@
 #include <QtCore/QObject>
 #include <QtCore/QProcess>
 #include <QtCore/QStringList>
+#include <QtCore/QPointer>
 
 QT_BEGIN_NAMESPACE
 class QFile;
@@ -64,6 +65,7 @@ class ChangeSelectionDialog;
 class GitSubmitEditor;
 struct CommitData;
 struct GitSettings;
+class StashDialog;
 
 class GitPlugin : public VCSBase::VCSBasePlugin
 {
@@ -104,6 +106,7 @@ private slots:
     void showCommit();
     void startCommit();
     void stash();
+    void stashSnapshot();
     void stashPop();
     void branchList();
     void stashList();
@@ -145,6 +148,7 @@ private:
     QAction *m_undoAction;
     QAction *m_redoAction;
     QAction *m_stashAction;
+    QAction *m_stashSnapshotAction;
     QAction *m_stashPopAction;
     QAction *m_stashListAction;
     QAction *m_branchListAction;
@@ -152,11 +156,13 @@ private:
 
     GitClient                   *m_gitClient;
     ChangeSelectionDialog       *m_changeSelectionDialog;
+    QPointer<StashDialog>       m_stashDialog;
     QString                     m_submitRepository;
     QStringList                 m_submitOrigCommitFiles;
     QStringList                 m_submitOrigDeleteFiles;
     QString                     m_commitMessageFileName;
     bool                        m_submitActionTriggered;
+
 };
 
 } // namespace Git
diff --git a/src/plugins/git/gitutils.cpp b/src/plugins/git/gitutils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c27a64f5b0279977b1cf08e8f9722ca564e9644
--- /dev/null
+++ b/src/plugins/git/gitutils.cpp
@@ -0,0 +1,104 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "gitutils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+#include <QtGui/QInputDialog>
+#include <QtGui/QLineEdit>
+
+namespace Git {
+namespace Internal {
+
+QDebug operator<<(QDebug d, const Stash &s)
+{
+    QDebug nospace = d.nospace();
+    nospace << "name=" << s.name << " branch=" << s.branch << " message=" << s.message;
+    return d;
+}
+
+void Stash::clear()
+{
+    name.clear();
+    branch.clear();
+    message.clear();
+}
+
+/* Parse a stash line in its 2 manifestations (with message/without message containing
+ * <base_sha1>+subject):
+\code
+stash@{1}: WIP on <branch>: <base_sha1> <subject_base_sha1>
+stash@{2}: On <branch>: <message>
+\endcode */
+
+bool Stash::parseStashLine(const QString &l)
+{
+    const QChar colon = QLatin1Char(':');
+    const int branchPos = l.indexOf(colon);
+    if (branchPos < 0)
+        return false;
+    const int messagePos = l.indexOf(colon, branchPos + 1);
+    if (messagePos < 0)
+        return false;
+    // Name
+    const QString newName = l.left(branchPos);
+    // Branch spec
+    const QString branchSpec = l.mid(branchPos + 1, messagePos - branchPos - 1);
+    const bool emptyMessage = branchSpec.contains(QLatin1String("WIP")); // "Work in Progress or sth"
+    const int onIndex = branchSpec.indexOf(QLatin1String("on "), 0, Qt::CaseInsensitive);
+    if (onIndex == -1)
+        return false;
+    const QString newBranch = branchSpec.mid(onIndex + 3);
+    // Happy!
+    name = newName;
+    branch = newBranch;
+    if (!emptyMessage)
+        message = l.mid(messagePos + 2); // skip blank
+    return true;
+}
+
+// Make QInputDialog  play nicely, widen it a bit.
+bool inputText(QWidget *parent, const QString &title, const QString &prompt, QString *s)
+{
+    QInputDialog dialog(parent);
+    dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
+    dialog.setWindowTitle(title);
+    dialog.setLabelText(prompt);
+    dialog.setTextValue(*s);
+    // Nasty hack:
+    if (QLineEdit *le = qFindChild<QLineEdit*>(&dialog))
+        le->setMinimumWidth(500);
+    if (dialog.exec() != QDialog::Accepted)
+        return false;
+    *s = dialog.textValue();
+    return true;
+}
+} // namespace Internal
+} // namespace Git
diff --git a/src/plugins/git/gitutils.h b/src/plugins/git/gitutils.h
new file mode 100644
index 0000000000000000000000000000000000000000..16d80ca656c28061e7f6fa2598f1382adca26bc4
--- /dev/null
+++ b/src/plugins/git/gitutils.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef GITUTILS_H
+#define GITUTILS_H
+
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Git {
+namespace Internal {
+
+struct Stash {
+    void clear();
+    bool parseStashLine(const QString &l);
+
+    QString name;
+    QString branch;
+    QString message;
+};
+
+QDebug operator<<(QDebug d, const Stash &);
+
+// Make QInputDialog  play nicely
+bool inputText(QWidget *parent, const QString &title, const QString &prompt, QString *s);
+
+} // namespace Internal
+} // namespace Git
+
+#endif // GITUTILS_H
diff --git a/src/plugins/git/gitversioncontrol.cpp b/src/plugins/git/gitversioncontrol.cpp
index 76541548ccbbf0949be4f41813265fbb37df51ca..c98f06889217ab8269bc458da274a8d9f9fceb9f 100644
--- a/src/plugins/git/gitversioncontrol.cpp
+++ b/src/plugins/git/gitversioncontrol.cpp
@@ -30,10 +30,19 @@
 #include "gitversioncontrol.h"
 #include "gitclient.h"
 #include "gitplugin.h"
+#include "gitutils.h"
+
+static const char stashMessageKeywordC[] = "IVersionControl@";
+static const char stashRevisionIdC[] = "revision";
 
 namespace Git {
 namespace Internal {
 
+static inline GitClient *gitClient()
+{
+    return GitPlugin::instance()->gitClient();
+}
+
 GitVersionControl::GitVersionControl(GitClient *client) :
     m_enabled(true),
     m_client(client)
@@ -54,6 +63,7 @@ bool GitVersionControl::supportsOperation(Operation operation) const
     case OpenOperation:
         break;
     case CreateRepositoryOperation:
+    case SnapshotOperations:
         rc = true;
         break;
     }
@@ -78,7 +88,91 @@ bool GitVersionControl::vcsDelete(const QString & /*fileName*/)
 
 bool GitVersionControl::vcsCreateRepository(const QString &directory)
 {
-    return GitPlugin::instance()->gitClient()->synchronousInit(directory);
+    return gitClient()->synchronousInit(directory);
+}
+/* Snapshots are implement using stashes, relying on stash messages for
+ * naming as the actual stash names (stash{n}) are rotated as one adds stashes.
+ * Note that the snapshot interface does not care whether we have an unmodified
+ * repository state, in which case git refuses to stash.
+ * In that case, return a special identifier as "specialprefix:<branch>:<head revision>",
+ * which will trigger a checkout in restore(). */
+
+QString GitVersionControl::vcsCreateSnapshot(const QString &topLevel)
+{
+    bool repositoryUnchanged;
+    // Create unique keyword
+    static int n = 1;
+    QString keyword = QLatin1String(stashMessageKeywordC) + QString::number(n++);
+    const QString stashMessage =
+            gitClient()->synchronousStash(topLevel, keyword,
+                                          GitClient::StashImmediateRestore|GitClient::StashIgnoreUnchanged,
+                                          &repositoryUnchanged);
+    if (!stashMessage.isEmpty())
+        return stashMessage;
+    if (repositoryUnchanged) {
+        // For unchanged repository state: return identifier + top revision
+        QString topRevision;
+        QString branch;
+        if (!gitClient()->synchronousTopRevision(topLevel, &topRevision, &branch))
+            return QString();
+        const QChar colon = QLatin1Char(':');
+        QString id = QLatin1String(stashRevisionIdC);
+        id += colon;
+        id += branch;
+        id += colon;
+        id += topRevision;
+        return id;
+    }
+    return QString(); // Failure
+}
+
+QStringList GitVersionControl::vcsSnapshots(const QString &topLevel)
+{
+    QList<Stash> stashes;
+    if (!gitClient()->synchronousStashList(topLevel, &stashes))
+        return QStringList();
+    // Return the git stash 'message' as identifier, ignoring empty ones
+    QStringList rc;
+    foreach(const Stash &s, stashes)
+        if (!s.message.isEmpty())
+            rc.push_back(s.message);
+    return rc;
+}
+
+bool GitVersionControl::vcsRestoreSnapshot(const QString &topLevel, const QString &name)
+{
+    bool success = false;
+    do {
+        // Is this a revision or a stash
+        if (name.startsWith(QLatin1String(stashRevisionIdC))) {
+            // Restore "id:branch:revision"
+            const QStringList tokens = name.split(QLatin1Char(':'));
+            if (tokens.size() != 3)
+                break;
+            const QString branch = tokens.at(1);
+            const QString revision = tokens.at(2);
+            success = gitClient()->synchronousReset(topLevel)
+                      && gitClient()->synchronousCheckoutBranch(topLevel, branch)
+                      && gitClient()->synchronousCheckoutFiles(topLevel, QStringList(), revision);
+        } else {
+            // Restore stash if it can be resolved.
+            QString stashName;
+            success = gitClient()->stashNameFromMessage(topLevel, name, &stashName)
+                      && gitClient()->synchronousReset(topLevel)
+                      && gitClient()->synchronousStashRestore(topLevel, stashName);
+        }
+    }  while (false);
+    return success;
+}
+
+bool GitVersionControl::vcsRemoveSnapshot(const QString &topLevel, const QString &name)
+{
+    // Is this a revision -> happy
+    if (name.startsWith(QLatin1String(stashRevisionIdC)))
+        return true;
+    QString stashName;
+    return gitClient()->stashNameFromMessage(topLevel, name, &stashName)
+            && gitClient()->synchronousStashRemove(topLevel, stashName);
 }
 
 bool GitVersionControl::managesDirectory(const QString &directory) const
@@ -96,5 +190,10 @@ void GitVersionControl::emitFilesChanged(const QStringList &l)
     emit filesChanged(l);
 }
 
+void GitVersionControl::emitRepositoryChanged(const QString &r)
+{
+    emit repositoryChanged(r);
+}
+
 } // Internal
 } // Git
diff --git a/src/plugins/git/gitversioncontrol.h b/src/plugins/git/gitversioncontrol.h
index e5013cce6e09a2116ff4c2245567babae6511e16..88927fdf14f312f290c2b2a17e40c02f19110b5f 100644
--- a/src/plugins/git/gitversioncontrol.h
+++ b/src/plugins/git/gitversioncontrol.h
@@ -54,11 +54,13 @@ public:
     virtual bool vcsAdd(const QString &fileName);
     virtual bool vcsDelete(const QString &filename);
     virtual bool vcsCreateRepository(const QString &directory);
+    virtual QString vcsCreateSnapshot(const QString &topLevel);
+    virtual QStringList vcsSnapshots(const QString &topLevel);
+    virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
+    virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
 
     void emitFilesChanged(const QStringList &);
-
-signals:
-    void enabledChanged(bool);
+    void emitRepositoryChanged(const QString &);
 
 private:
     bool m_enabled;
diff --git a/src/plugins/git/stashdialog.cpp b/src/plugins/git/stashdialog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a39559ad597b964fbf7882a1e5ad289492b986b2
--- /dev/null
+++ b/src/plugins/git/stashdialog.cpp
@@ -0,0 +1,411 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "stashdialog.h"
+#include "gitclient.h"
+#include "gitplugin.h"
+#include "gitutils.h"
+#include "ui_stashdialog.h"
+
+#include <utils/qtcassert.h>
+#include <vcsbase/vcsbaseoutputwindow.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QModelIndex>
+#include <QtCore/QDateTime>
+#include <QtGui/QStandardItemModel>
+#include <QtGui/QSortFilterProxyModel>
+#include <QtGui/QItemSelectionModel>
+#include <QtGui/QMessageBox>
+#include <QtGui/QPushButton>
+
+enum { NameColumn, BranchColumn, MessageColumn, ColumnCount };
+
+namespace Git {
+namespace Internal {
+
+static inline GitClient *gitClient()
+{
+    return GitPlugin::instance()->gitClient();
+}
+
+static inline QList<QStandardItem*> stashModelRowItems(const Stash &s)
+{
+    Qt::ItemFlags itemFlags = Qt::ItemIsSelectable|Qt::ItemIsEnabled;
+    QStandardItem *nameItem = new QStandardItem(s.name);
+    nameItem->setFlags(itemFlags);
+    QStandardItem *branchItem = new QStandardItem(s.branch);
+    branchItem->setFlags(itemFlags);
+    QStandardItem *messageItem = new QStandardItem(s.message);
+    messageItem->setFlags(itemFlags);
+    QList<QStandardItem*> rc;
+    rc << nameItem << branchItem << messageItem;
+    return rc;
+}
+
+// -----------  StashModel
+class StashModel : public QStandardItemModel {
+public:
+    explicit StashModel(QObject *parent = 0);
+
+    void setStashes(const QList<Stash> &stashes);
+    const Stash &at(int i) { return m_stashes.at(i); }
+
+private:
+    QList<Stash> m_stashes;
+};
+
+StashModel::StashModel(QObject *parent) :
+    QStandardItemModel(0, ColumnCount, parent)
+{
+    QStringList headers;
+    headers << StashDialog::tr("Name") << StashDialog::tr("Branch") << StashDialog::tr("Message");
+    setHorizontalHeaderLabels(headers);
+}
+
+void StashModel::setStashes(const QList<Stash> &stashes)
+{
+    m_stashes = stashes;
+    if (const int rows = rowCount())
+        removeRows(0, rows);
+    foreach(const Stash &s, stashes)
+        appendRow(stashModelRowItems(s));
+}
+
+// ---------- StashDialog
+StashDialog::StashDialog(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::StashDialog),
+    m_model(new StashModel),
+    m_proxyModel(new QSortFilterProxyModel),
+    m_deleteAllButton(new QPushButton(tr("Delete all..."))),
+    m_deleteSelectionButton(new QPushButton(tr("Delete..."))),
+    m_showCurrentButton(new QPushButton(tr("Show"))),
+    m_restoreCurrentButton(new QPushButton(tr("Restore..."))),
+    m_restoreCurrentInBranchButton(new QPushButton(tr("Restore to branch..."))),
+    m_refreshButton(new QPushButton(tr("Refresh")))
+{
+    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+    ui->setupUi(this);
+    // Buttons
+    ui->buttonBox->addButton(m_showCurrentButton, QDialogButtonBox::ActionRole);
+    connect(m_showCurrentButton, SIGNAL(clicked()), this, SLOT(showCurrent()));
+    ui->buttonBox->addButton(m_refreshButton, QDialogButtonBox::ActionRole);
+    connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(forceRefresh()));
+    ui->buttonBox->addButton(m_restoreCurrentButton, QDialogButtonBox::ActionRole);
+    connect(m_restoreCurrentButton, SIGNAL(clicked()), this, SLOT(restoreCurrent()));
+    ui->buttonBox->addButton(m_restoreCurrentInBranchButton, QDialogButtonBox::ActionRole);
+    connect(m_restoreCurrentInBranchButton, SIGNAL(clicked()), this, SLOT(restoreCurrentInBranch()));
+    ui->buttonBox->addButton(m_deleteSelectionButton, QDialogButtonBox::ActionRole);
+    connect(m_deleteSelectionButton, SIGNAL(clicked()), this, SLOT(deleteSelection()));
+    ui->buttonBox->addButton(m_deleteAllButton, QDialogButtonBox::ActionRole);
+    connect(m_deleteAllButton, SIGNAL(clicked()), this, SLOT(deleteAll()));
+    // Models
+    m_proxyModel->setSourceModel(m_model);
+    m_proxyModel->setFilterKeyColumn(-1);
+    m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+    ui->stashView->setModel(m_proxyModel);
+    ui->stashView->setSelectionMode(QAbstractItemView::MultiSelection);
+    connect(ui->filterLineEdit, SIGNAL(filterChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString)));
+    connect(ui->stashView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
+            this, SLOT(enableButtons()));
+    connect(ui->stashView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+            this, SLOT(enableButtons()));
+    connect(ui->stashView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(showCurrent()));
+    ui->stashView->setFocus();
+}
+
+StashDialog::~StashDialog()
+{
+    delete ui;
+}
+
+void StashDialog::changeEvent(QEvent *e)
+{
+    QDialog::changeEvent(e);
+    switch (e->type()) {
+    case QEvent::LanguageChange:
+        ui->retranslateUi(this);
+        break;
+    default:
+        break;
+    }
+}
+
+void StashDialog::refresh(const QString &repository, bool force)
+{
+    if (m_repository == repository && !force)
+        return;
+    // Refresh
+    m_repository = repository;
+    if (m_repository.isEmpty()) {
+        ui->repositoryLabel->setText(tr("<No repository>"));
+        m_model->setStashes(QList<Stash>());
+    } else {
+        ui->repositoryLabel->setText(tr("Repository: %1").arg(repository));
+        QList<Stash> stashes;
+        gitClient()->synchronousStashList(m_repository, &stashes);
+        m_model->setStashes(stashes);
+        if (!stashes.isEmpty()) {
+            for(int c = 0; c < ColumnCount; c++)
+                ui->stashView->resizeColumnToContents(c);
+        }
+    }
+    enableButtons();
+}
+
+void StashDialog::deleteAll()
+{
+    const QString title = tr("Delete stashes");
+    if (!ask(title, tr("Do you want to delete all stashes?")))
+        return;
+    QString errorMessage;
+    if (gitClient()->synchronousStashRemove(m_repository, QString(), &errorMessage)) {
+        refresh(m_repository, true);
+    } else {
+        warning(title, errorMessage);
+    }
+}
+
+void StashDialog::deleteSelection()
+{
+    const QList<int> rows = selectedRows();
+    QTC_ASSERT(!rows.isEmpty(), return)
+    const QString title = tr("Delete stashes");
+    if (!ask(title, tr("Do you want to delete %n stash(es)?", 0, rows.size())))
+        return;
+    QString errorMessage;
+    QStringList errors;
+    // Delete in reverse order as stashes rotate
+    for (int r = rows.size() - 1; r >= 0; r--)
+        if (!gitClient()->synchronousStashRemove(m_repository, m_model->at(r).name, &errorMessage))
+            errors.push_back(errorMessage);
+    refresh(m_repository, true);
+    if (!errors.isEmpty())
+        warning(title, errors.join(QString(QLatin1Char('\n'))));
+}
+
+void StashDialog::showCurrent()
+{
+    const int index = currentRow();
+    QTC_ASSERT(index >= 0, return)
+    gitClient()->show(m_repository, m_model->at(index).name);
+}
+
+// Suggest Branch name to restore 'stash@{0}' -> 'stash0-date'
+static inline QString stashRestoreDefaultBranch(QString stash)
+{
+    stash.remove(QLatin1Char('{'));
+    stash.remove(QLatin1Char('}'));
+    stash.remove(QLatin1Char('@'));
+    stash += QLatin1Char('-');
+    stash += QDateTime::currentDateTime().toString(QLatin1String("yyMMddhhmmss"));
+    return stash;
+}
+
+// Return next stash id 'stash@{0}' -> 'stash@{1}'
+static inline QString nextStash(const QString &stash)
+{
+    const int openingBracePos = stash.indexOf(QLatin1Char('{'));
+    if (openingBracePos == -1)
+        return QString();
+    const int closingBracePos = stash.indexOf(QLatin1Char('}'), openingBracePos + 2);
+    if (closingBracePos == -1)
+        return QString();
+    bool ok;
+    const int n = stash.mid(openingBracePos + 1, closingBracePos - openingBracePos - 1).toInt(&ok);
+    if (!ok)
+        return QString();
+    QString rc =  stash.left(openingBracePos + 1);
+    rc += QString::number(n + 1);
+    rc += QLatin1Char('}');
+    return rc;
+}
+
+StashDialog::ModifiedRepositoryAction StashDialog::promptModifiedRepository(const QString &stash)
+{
+    QMessageBox box(QMessageBox::Question,
+                    tr("Repository modified"),
+                    tr("%1 cannot be restored since the repository is modified.\n"
+                       "You can choose between stashing the changes or discarding them.").arg(stash),
+                    QMessageBox::Cancel, this);
+    QPushButton *stashButton = box.addButton(tr("Stash"), QMessageBox::AcceptRole);
+    QPushButton *discardButton = box.addButton(tr("Discard"), QMessageBox::AcceptRole);
+    box.exec();
+    const QAbstractButton *clickedButton = box.clickedButton();
+    if (clickedButton == stashButton)
+        return ModifiedRepositoryStash;
+    if (clickedButton == discardButton)
+        return ModifiedRepositoryDiscard;
+    return ModifiedRepositoryCancel;
+}
+
+// Prompt for restore: Make sure repository is unmodified,
+// prompt for a branch if desired or just ask to restore.
+// Note that the stash to be restored changes if the user
+// chooses to stash away modified repository.
+bool StashDialog::promptForRestore(QString *stash,
+                                   QString *branch /* = 0*/,
+                                   QString *errorMessage)
+{
+    const QString stashIn = *stash;
+    bool modifiedPromptShown = false;
+    switch (gitClient()->gitStatus(m_repository, false, 0, errorMessage)) {
+    case GitClient::StatusFailed:
+        return false;
+    case GitClient::StatusChanged: {
+            switch (promptModifiedRepository(*stash)) {
+            case ModifiedRepositoryCancel:
+                return false;
+            case ModifiedRepositoryStash:
+                if (gitClient()->synchronousStash(m_repository, QString(), GitClient::StashPromptDescription).isEmpty())
+                    return false;
+                *stash = nextStash(*stash); // Our stash id to be restored changed
+                QTC_ASSERT(!stash->isEmpty(), return false)
+                break;
+            case ModifiedRepositoryDiscard:
+                if (!gitClient()->synchronousReset(m_repository))
+                    return false;
+                break;
+            }
+        modifiedPromptShown = true;
+    }
+        break;
+    case GitClient::StatusUnchanged:
+        break;
+    }
+    // Prompt for branch or just ask.
+    if (branch) {
+        *branch = stashRestoreDefaultBranch(*stash);
+        if (!inputText(this, tr("Restore Stash to Branch"), tr("Branch:"), branch)
+            || branch->isEmpty())
+            return false;
+    } else {
+        if (!modifiedPromptShown && !ask(tr("Stash Restore"), tr("Would you like to restore %1?").arg(stashIn)))
+            return false;
+    }
+    return true;
+}
+
+static inline QString msgRestoreFailedTitle(const QString &stash)
+{
+    return StashDialog::tr("Error restoring %1").arg(stash);
+}
+
+void StashDialog::restoreCurrent()
+{
+    const int index = currentRow();
+    QTC_ASSERT(index >= 0, return)
+    QString errorMessage;
+    QString name = m_model->at(index).name;
+    // Make sure repository is not modified, restore. The command will
+    // output to window on success.
+    const bool success = promptForRestore(&name, 0, &errorMessage)
+                         && gitClient()->synchronousStashRestore(m_repository, name, QString(), &errorMessage);
+    if (success) {
+        refresh(m_repository, true); // Might have stashed away local changes.
+    } else {
+        if (!errorMessage.isEmpty())
+        warning(msgRestoreFailedTitle(name), errorMessage);
+    }
+}
+
+void StashDialog::restoreCurrentInBranch()
+{
+    const int index = currentRow();
+    QTC_ASSERT(index >= 0, return)
+            QString errorMessage;
+    QString branch;
+    QString name = m_model->at(index).name;
+    const bool success = promptForRestore(&name, &branch, &errorMessage)
+                         && gitClient()->synchronousStashRestore(m_repository, name, branch, &errorMessage);
+    if (success) {
+        refresh(m_repository, true); // git deletes the stash, unfortunately.
+    } else {
+        if (!errorMessage.isEmpty())
+            warning(msgRestoreFailedTitle(name), errorMessage);
+    }
+}
+
+int StashDialog::currentRow() const
+{
+    const QModelIndex proxyIndex = ui->stashView->currentIndex();
+    if (proxyIndex.isValid()) {
+        const QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
+        if (index.isValid())
+            return index.row();
+    }
+    return -1;
+}
+
+QList<int> StashDialog::selectedRows() const
+{
+    QList<int> rc;
+    foreach(const QModelIndex &proxyIndex, ui->stashView->selectionModel()->selectedRows()) {
+        const QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
+        if (index.isValid())
+            rc.push_back(index.row());
+    }
+    qSort(rc);
+    return rc;
+}
+
+void StashDialog::forceRefresh()
+{
+    refresh(m_repository, true);
+}
+
+void StashDialog::enableButtons()
+{
+    const bool hasStashes = m_model->rowCount();
+    const bool hasCurrentRow = hasStashes && currentRow() >= 0;
+    m_deleteAllButton->setEnabled(hasStashes);
+    m_showCurrentButton->setEnabled(hasCurrentRow);
+    m_restoreCurrentButton->setEnabled(hasCurrentRow);
+    m_restoreCurrentInBranchButton->setEnabled(hasCurrentRow);
+    const bool hasSelection = !ui->stashView->selectionModel()->selectedRows().isEmpty();
+    m_deleteSelectionButton->setEnabled(hasSelection);
+}
+
+void StashDialog::warning(const QString &title, const QString &what, const QString &details)
+{
+    QMessageBox msgBox(QMessageBox::Warning, title, what, QMessageBox::Ok, this);
+    if (!details.isEmpty())
+        msgBox.setDetailedText(details);
+    msgBox.exec();
+}
+
+bool StashDialog::ask(const QString &title, const QString &what, bool defaultButton)
+{
+    return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
+                                 defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes;
+}
+
+} // namespace Internal
+} // namespace Git
diff --git a/src/plugins/git/stashdialog.h b/src/plugins/git/stashdialog.h
new file mode 100644
index 0000000000000000000000000000000000000000..377fef41e88be469d59d43412359ae01fae1386d
--- /dev/null
+++ b/src/plugins/git/stashdialog.h
@@ -0,0 +1,105 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef STASHDIALOG_H
+#define STASHDIALOG_H
+
+#include <QtGui/QDialog>
+
+QT_BEGIN_NAMESPACE
+class QSortFilterProxyModel;
+class QPushButton;
+class QModelIndex;
+QT_END_NAMESPACE
+
+namespace Git {
+namespace Internal {
+
+namespace Ui {
+    class StashDialog;
+}
+class StashModel;
+
+/* StashDialog: Non-modal dialog that manages the list of stashes
+ * of the repository. Offers to show, restore, restore to branch
+ * (in case restore fails due to conflicts) on current and
+ * delete on selection/all. */
+
+class StashDialog : public QDialog {
+    Q_OBJECT
+public:
+    explicit StashDialog(QWidget *parent = 0);
+    ~StashDialog();
+
+public slots:
+    void refresh(const QString &repository, bool force);
+
+protected:
+    void changeEvent(QEvent *e);
+
+private slots:
+    void deleteAll();
+    void deleteSelection();
+    void showCurrent();
+    void restoreCurrent();
+    void restoreCurrentInBranch();
+    void enableButtons();
+    void forceRefresh();
+
+private:
+    // Prompt dialog for modified repositories. Ask to undo or stash away.
+    enum ModifiedRepositoryAction {
+        ModifiedRepositoryCancel,
+        ModifiedRepositoryStash,
+        ModifiedRepositoryDiscard
+    };
+
+    ModifiedRepositoryAction promptModifiedRepository(const QString &stash);
+    bool promptForRestore(QString *stash, QString *branch /* = 0 */, QString *errorMessage);
+    bool ask(const QString &title, const QString &what, bool defaultButton = true);
+    void warning(const QString &title, const QString &what, const QString &details = QString());
+    int currentRow() const;
+    QList<int> selectedRows() const;    \
+
+    Ui::StashDialog *ui;
+    StashModel *m_model;
+    QSortFilterProxyModel *m_proxyModel;
+    QPushButton *m_deleteAllButton;
+    QPushButton *m_deleteSelectionButton;
+    QPushButton *m_showCurrentButton;
+    QPushButton *m_restoreCurrentButton;
+    QPushButton *m_restoreCurrentInBranchButton;
+    QPushButton *m_refreshButton;
+    QString m_repository;
+};
+
+} // namespace Internal
+} // namespace Git
+
+#endif // STASHDIALOG_H
diff --git a/src/plugins/git/stashdialog.ui b/src/plugins/git/stashdialog.ui
new file mode 100644
index 0000000000000000000000000000000000000000..323175570108e9b474697b6a20847c1b70a2a402
--- /dev/null
+++ b/src/plugins/git/stashdialog.ui
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Git::Internal::StashDialog</class>
+ <widget class="QDialog" name="Git::Internal::StashDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>599</width>
+    <height>485</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Stashes</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="repositoryLabel">
+       <property name="text">
+        <string notr="true">Repository: Dummy</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Utils::FilterLineEdit" name="filterLineEdit"/>
+     </item>
+     <item>
+      <widget class="QTreeView" name="stashView"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Utils::FancyLineEdit</class>
+   <extends>QLineEdit</extends>
+   <header location="global">utils/fancylineedit.h</header>
+  </customwidget>
+  <customwidget>
+   <class>Utils::FilterLineEdit</class>
+   <extends>Utils::FancyLineEdit</extends>
+   <header location="global">utils/filterlineedit.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>Git::Internal::StashDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>Git::Internal::StashDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/mercurial/mercurialcontrol.cpp b/src/plugins/mercurial/mercurialcontrol.cpp
index 3ca94573be47988b2b4a63f014bc0ec5680d5530..a0af2093104df5a5e7bb0c2cdcc6ba9ae0cabc19 100644
--- a/src/plugins/mercurial/mercurialcontrol.cpp
+++ b/src/plugins/mercurial/mercurialcontrol.cpp
@@ -68,6 +68,7 @@ bool MercurialControl::supportsOperation(Operation operation) const
     case Core::IVersionControl::CreateRepositoryOperation:
         break;
     case Core::IVersionControl::OpenOperation:
+    case Core::IVersionControl::SnapshotOperations:
         supported = false;
         break;
     }
@@ -97,6 +98,26 @@ bool MercurialControl::vcsCreateRepository(const QString &directory)
     return mercurialClient->createRepositorySync(directory);
 }
 
+QString MercurialControl::vcsCreateSnapshot(const QString &)
+{
+    return QString();
+}
+
+QStringList MercurialControl::vcsSnapshots(const QString &)
+{
+    return QStringList();
+}
+
+bool MercurialControl::vcsRestoreSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
+bool MercurialControl::vcsRemoveSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
 bool MercurialControl::sccManaged(const QString &filename)
 {
     const QFileInfo fi(filename);
diff --git a/src/plugins/mercurial/mercurialcontrol.h b/src/plugins/mercurial/mercurialcontrol.h
index d447a5bc1643c0651be07f23b4c1f4828ec4d296..c82401bad89804cc4baa28b6f5851d3e1da83b16 100644
--- a/src/plugins/mercurial/mercurialcontrol.h
+++ b/src/plugins/mercurial/mercurialcontrol.h
@@ -57,6 +57,10 @@ public:
     bool vcsAdd(const QString &filename);
     bool vcsDelete(const QString &filename);
     bool vcsCreateRepository(const QString &directory);
+    QString vcsCreateSnapshot(const QString &topLevel);
+    QStringList vcsSnapshots(const QString &topLevel);
+    bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
+    bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
     bool sccManaged(const QString &filename);
 
 public slots:
diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp
index 0be2ef59019e472ef078c7b5f151e1ff37f7a16a..7afd5815fea5479e4660be11f46700645159bcea 100644
--- a/src/plugins/perforce/perforceversioncontrol.cpp
+++ b/src/plugins/perforce/perforceversioncontrol.cpp
@@ -56,6 +56,7 @@ bool PerforceVersionControl::supportsOperation(Operation operation) const
     case OpenOperation:
         return true;
     case CreateRepositoryOperation:
+    case SnapshotOperations:
         break;
     }
     return false;
@@ -84,6 +85,26 @@ bool PerforceVersionControl::vcsCreateRepository(const QString &)
     return false;
 }
 
+QString PerforceVersionControl::vcsCreateSnapshot(const QString &)
+{
+    return QString();
+}
+
+QStringList PerforceVersionControl::vcsSnapshots(const QString &)
+{
+    return QStringList();
+}
+
+bool PerforceVersionControl::vcsRestoreSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
+bool PerforceVersionControl::vcsRemoveSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
 bool PerforceVersionControl::managesDirectory(const QString &directory) const
 {
     const bool rc = m_plugin->managesDirectory(directory);
diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h
index 815b743053976815c1d50c64032aa983b196e0a2..ee31794c7c5dfa190e864f4db62f313c616ce034 100644
--- a/src/plugins/perforce/perforceversioncontrol.h
+++ b/src/plugins/perforce/perforceversioncontrol.h
@@ -53,6 +53,10 @@ public:
     virtual bool vcsAdd(const QString &fileName);
     virtual bool vcsDelete(const QString &filename);
     virtual bool vcsCreateRepository(const QString &directory);
+    virtual QString vcsCreateSnapshot(const QString &topLevel);
+    virtual QStringList vcsSnapshots(const QString &topLevel);
+    virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
+    virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
 
     void emitRepositoryChanged(const QString &s);
     void emitFilesChanged(const QStringList &l);
diff --git a/src/plugins/subversion/subversioncontrol.cpp b/src/plugins/subversion/subversioncontrol.cpp
index f8b795e5f48a508b34e42b7bc5dfb23a62bd85a8..99cbdc6883e41e8e21d948001d3a79938616a383 100644
--- a/src/plugins/subversion/subversioncontrol.cpp
+++ b/src/plugins/subversion/subversioncontrol.cpp
@@ -55,6 +55,7 @@ bool SubversionControl::supportsOperation(Operation operation) const
         break;
     case OpenOperation:
     case CreateRepositoryOperation:
+    case SnapshotOperations:
         rc = false;
         break;
     }
@@ -84,6 +85,26 @@ bool SubversionControl::vcsCreateRepository(const QString &)
     return false;
 }
 
+QString SubversionControl::vcsCreateSnapshot(const QString &)
+{
+    return QString();
+}
+
+QStringList SubversionControl::vcsSnapshots(const QString &)
+{
+    return QStringList();
+}
+
+bool SubversionControl::vcsRestoreSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
+bool SubversionControl::vcsRemoveSnapshot(const QString &, const QString &)
+{
+    return false;
+}
+
 bool SubversionControl::managesDirectory(const QString &directory) const
 {
     return m_plugin->managesDirectory(directory);
diff --git a/src/plugins/subversion/subversioncontrol.h b/src/plugins/subversion/subversioncontrol.h
index 5aa60373acee78d72f5997768cef6553ea0aa2ed..7a3aec8c83f489dfa42bf5b4dce9f494e471671c 100644
--- a/src/plugins/subversion/subversioncontrol.h
+++ b/src/plugins/subversion/subversioncontrol.h
@@ -54,6 +54,11 @@ public:
     virtual bool vcsDelete(const QString &filename);
     virtual bool vcsCreateRepository(const QString &directory);
 
+    virtual QString vcsCreateSnapshot(const QString &topLevel);
+    virtual QStringList vcsSnapshots(const QString &topLevel);
+    virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
+    virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
+
     void emitRepositoryChanged(const QString &);
     void emitFilesChanged(const QStringList &);
 
diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp
index 0cb54a70087d6cc09871ae4e066661c650774ede..ea701e6a22062ae01e3c5b9c1ae0c7d5b8690578 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.cpp
+++ b/src/plugins/vcsbase/vcsbaseplugin.cpp
@@ -30,6 +30,7 @@
 #include "vcsbaseplugin.h"
 #include "vcsbasesubmiteditor.h"
 #include "vcsplugin.h"
+#include "vcsbaseoutputwindow.h"
 #include "corelistener.h"
 
 #include <coreplugin/icore.h>
@@ -353,6 +354,11 @@ struct VCSBasePluginPrivate {
     Core::IVersionControl *m_versionControl;
     VCSBasePluginState m_state;
     int m_actionState;
+    QAction *m_testSnapshotAction;
+    QAction *m_testListSnapshotsAction;
+    QAction *m_testRestoreSnapshotAction;
+    QAction *m_testRemoveSnapshotAction;
+    QString m_testLastSnapshot;
 
     static Internal::StateListener *m_listener;
 };
@@ -360,7 +366,11 @@ struct VCSBasePluginPrivate {
 VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorId) :
     m_submitEditorId(submitEditorId),
     m_versionControl(0),
-    m_actionState(-1)
+    m_actionState(-1),
+    m_testSnapshotAction(0),
+    m_testListSnapshotsAction(0),
+    m_testRestoreSnapshotAction(0),
+    m_testRemoveSnapshotAction(0)
 {
 }
 
@@ -507,6 +517,62 @@ void VCSBasePlugin::createRepository()
     }
 }
 
+// For internal tests: Create actions driving IVersionControl's snapshot interface.
+QList<QAction*> VCSBasePlugin::createSnapShotTestActions()
+{
+    if (!d->m_testSnapshotAction) {
+        d->m_testSnapshotAction = new QAction(QLatin1String("Take snapshot"), this);
+        connect(d->m_testSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestSnapshot()));
+        d->m_testListSnapshotsAction = new QAction(QLatin1String("List snapshots"), this);
+        connect(d->m_testListSnapshotsAction, SIGNAL(triggered()), this, SLOT(slotTestListSnapshots()));
+        d->m_testRestoreSnapshotAction = new QAction(QLatin1String("Restore snapshot"), this);
+        connect(d->m_testRestoreSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRestoreSnapshot()));
+        d->m_testRemoveSnapshotAction = new QAction(QLatin1String("Remove snapshot"), this);
+        connect(d->m_testRemoveSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRemoveSnapshot()));
+    }
+    QList<QAction*> rc;
+    rc << d->m_testSnapshotAction << d->m_testListSnapshotsAction << d->m_testRestoreSnapshotAction
+       << d->m_testRemoveSnapshotAction;
+    return rc;
+}
+
+void VCSBasePlugin::slotTestSnapshot()
+{
+    QTC_ASSERT(currentState().hasTopLevel(), return)
+    d->m_testLastSnapshot = versionControl()->vcsCreateSnapshot(currentState().topLevel());
+    qDebug() << "Snapshot " << d->m_testLastSnapshot;
+    VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshot: ") + d->m_testLastSnapshot);
+    if (!d->m_testLastSnapshot.isEmpty())
+        d->m_testRestoreSnapshotAction->setText(QLatin1String("Restore snapshot ") + d->m_testLastSnapshot);
+}
+
+void VCSBasePlugin::slotTestListSnapshots()
+{
+    QTC_ASSERT(currentState().hasTopLevel(), return)
+    const QStringList snapshots = versionControl()->vcsSnapshots(currentState().topLevel());
+    qDebug() << "Snapshots " << snapshots;
+    VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshots: ") + snapshots.join(QLatin1String(", ")));
+}
+
+void VCSBasePlugin::slotTestRestoreSnapshot()
+{
+    QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
+    const bool ok = versionControl()->vcsRestoreSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
+    const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" restored") : QLatin1String(" failed"));
+    qDebug() << msg;
+    VCSBaseOutputWindow::instance()->append(msg);
+}
+
+void VCSBasePlugin::slotTestRemoveSnapshot()
+{
+    QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
+    const bool ok = versionControl()->vcsRemoveSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
+    const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" removed") : QLatin1String(" failed"));
+    qDebug() << msg;
+    VCSBaseOutputWindow::instance()->append(msg);
+    d->m_testLastSnapshot.clear();
+}
+
 } // namespace VCSBase
 
 #include "vcsbaseplugin.moc"
diff --git a/src/plugins/vcsbase/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h
index a9351ed47039409cdfa888519b7d0dd4217b900e..4f4a6393dff87e668f1d6c85f436c2bab2c30634 100644
--- a/src/plugins/vcsbase/vcsbaseplugin.h
+++ b/src/plugins/vcsbase/vcsbaseplugin.h
@@ -34,7 +34,8 @@
 
 #include <extensionsystem/iplugin.h>
 
-#include <QSharedDataPointer>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QList>
 
 QT_BEGIN_NAMESPACE
 class QAction;
@@ -149,6 +150,9 @@ public:
     const VCSBasePluginState &currentState() const;
     Core::IVersionControl *versionControl() const;
 
+    // For internal tests: Create actions driving IVersionControl's snapshot interface.
+    QList<QAction*> createSnapShotTestActions();
+
 public slots:
     // Convenience slot for "Delete current file" action. Prompts to
     // delete the file via VCSManager.
@@ -175,6 +179,10 @@ protected:
 private slots:
     void slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result);
     void slotStateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
+    void slotTestSnapshot();
+    void slotTestListSnapshots();
+    void slotTestRestoreSnapshot();
+    void slotTestRemoveSnapshot();
 
 private:
     VCSBasePluginPrivate *d;
diff --git a/src/shared/qscripthighlighter/qscriptindenter.cpp b/src/shared/qscripthighlighter/qscriptindenter.cpp
index c697e16c69b07524c49313f6fb44a56fb6ae04d3..0abaf574dc92e0c9cd0cd61f0174db29d40f7d73 100644
--- a/src/shared/qscripthighlighter/qscriptindenter.cpp
+++ b/src/shared/qscripthighlighter/qscriptindenter.cpp
@@ -222,26 +222,9 @@ QString QScriptIndenter::trimmedCodeLine(const QString &t)
                 trimmed.append(QLatin1Char('X'));
 
         } else if (token.is(QScriptIncrementalScanner::Token::Comment)) {
-            int i = 0;
-            int e = token.length;
-
-            if (token.offset > 0 || startState == 0) {
-                trimmed.append(QLatin1String("/*"));
-                i += 2;
-            }
-
-            bool needEndOfComment = false;
-            if (e > 2 && token.end() == t.length() && scanner.endState() != 0) {
-                needEndOfComment = true;
-                e -= 2;
-            }
-
-            for (; i < e; ++i)
+            for (int i = 0; i < token.length; ++i)
                 trimmed.append(QLatin1Char(' '));
 
-            if (needEndOfComment)
-                trimmed.append(QLatin1String("*/"));
-
         } else {
             trimmed.append(t.midRef(token.offset, token.length));
         }
@@ -355,39 +338,6 @@ bool QScriptIndenter::readLine()
 
         yyLinizerState.line = trimmedCodeLine(yyLinizerState.line);
 
-        /*
-            Remove C-style comments that span multiple lines. If the
-            bottom line starts in a C-style comment, we are not aware
-            of that and eventually yyLine will contain a slash-aster.
-
-            Notice that both if's can be executed, since
-            yyLinizerState.inCComment is potentially set to false in
-            the first if. The order of the if's is also important.
-        */
-
-        if (yyLinizerState.inComment) {
-            const QLatin1String slashAster("/*");
-
-            k = yyLinizerState.line.indexOf(slashAster);
-            if (k == -1) {
-                yyLinizerState.line.clear();
-            } else {
-                yyLinizerState.line.truncate(k);
-                yyLinizerState.inComment = false;
-            }
-        }
-
-        if (!yyLinizerState.inComment) {
-            const QLatin1String asterSlash("*/");
-
-            k = yyLinizerState.line.indexOf(asterSlash);
-            if (k != -1) {
-                for (int i = 0; i < k + 2; i++)
-                    eraseChar(yyLinizerState.line, i, QLatin1Char(' '));
-                yyLinizerState.inComment = true;
-            }
-        }
-
         /*
             Remove preprocessor directives.
         */
@@ -447,7 +397,6 @@ bool QScriptIndenter::readLine()
 void QScriptIndenter::startLinizer()
 {
     yyLinizerState.braceDepth = 0;
-    yyLinizerState.inComment = false;
     yyLinizerState.pendingRightBrace = false;
 
     yyLine = &yyLinizerState.line;
@@ -497,30 +446,7 @@ int QScriptIndenter::indentWhenBottomLineStartsInMultiLineComment()
             break;
     }
 
-    const QString codeLine = trimmedCodeLine(blockText);
-
-    int k = codeLine.lastIndexOf(QLatin1String("/*"));
-    if (k == -1) {
-        /*
-          We found a normal text line in a comment. Align the
-          bottom line with the text on this line.
-        */
-        return indentOfLine(codeLine);
-    } else {
-        /*
-          The C-style comment starts on this line. If there is
-          text on the same line, align with it. Otherwise, align
-          with the slash-aster plus a given offset.
-        */
-        int indent = columnForIndex(codeLine, k);
-        k += 2;
-        while (k < yyLine->length()) {
-            if (!codeLine.at(k).isSpace())
-                return columnForIndex(codeLine, k);
-            k++;
-        }
-        return indent + ppCommentOffset;
-    }
+    return indentOfLine(blockText);
 }
 
 /*
diff --git a/src/shared/qscripthighlighter/qscriptindenter.h b/src/shared/qscripthighlighter/qscriptindenter.h
index 114e5c23f89934a1d32b8b14a2b6a3c4074c12ad..6bb0296c5a4f7b6d3a86d36d7319607157735cdc 100644
--- a/src/shared/qscripthighlighter/qscriptindenter.h
+++ b/src/shared/qscripthighlighter/qscriptindenter.h
@@ -94,13 +94,11 @@ private:
         LinizerState()
             : braceDepth(0),
               leftBraceFollows(false),
-              inComment(false),
               pendingRightBrace(false)
         { }
 
         int braceDepth;
         bool leftBraceFollows;
-        bool inComment;
         bool pendingRightBrace;
         QString line;
         QList<QScriptIncrementalScanner::Token> tokens;