1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "gitversioncontrol.h"
35 #include "gitclient.h"
36 #include "gitplugin.h"
39 #include <utils/qtcassert.h>
41 #include <QtCore/QDebug>
42 #include <QtCore/QFileInfo>
44 static const char stashMessageKeywordC[] = "IVersionControl@";
45 static const char stashRevisionIdC[] = "revision";
50 static inline GitClient *gitClient()
52 return GitPlugin::instance()->gitClient();
55 GitVersionControl::GitVersionControl(GitClient *client) :
61 QString GitVersionControl::displayName() const
63 return QLatin1String("git");
66 // Add: Implement using "git add --intent-to-add" starting from 1.6.1
67 static inline bool addOperationSupported()
69 return gitClient()->gitVersion(true) >= version(1, 6, 1);
72 bool GitVersionControl::supportsOperation(Operation operation) const
77 rc = addOperationSupported();
87 case CreateRepositoryOperation:
88 case SnapshotOperations:
91 case AnnotateOperation:
94 case CheckoutOperation:
95 case GetRepositoryRootOperation:
102 bool GitVersionControl::vcsOpen(const QString & /*fileName*/)
107 bool GitVersionControl::vcsAdd(const QString & fileName)
109 // Implement in terms of using "--intent-to-add"
110 QTC_ASSERT(addOperationSupported(), return false);
111 const QFileInfo fi(fileName);
112 return gitClient()->synchronousAdd(fi.absolutePath(), true, QStringList(fi.fileName()));
115 bool GitVersionControl::vcsDelete(const QString & fileName)
117 const QFileInfo fi(fileName);
118 return gitClient()->synchronousDelete(fi.absolutePath(), true, QStringList(fi.fileName()));
121 bool GitVersionControl::vcsMove(const QString &from, const QString &to)
123 const QFileInfo fromInfo(from);
124 const QFileInfo toInfo(to);
125 return gitClient()->synchronousMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
128 bool GitVersionControl::vcsCreateRepository(const QString &directory)
130 return gitClient()->synchronousInit(directory);
133 bool GitVersionControl::vcsCheckout(const QString &directory, const QByteArray &url)
135 return gitClient()->cloneRepository(directory,url);
138 QString GitVersionControl::vcsGetRepositoryURL(const QString &directory)
140 return gitClient()->vcsGetRepositoryURL(directory);
143 /* Snapshots are implement using stashes, relying on stash messages for
144 * naming as the actual stash names (stash{n}) are rotated as one adds stashes.
145 * Note that the snapshot interface does not care whether we have an unmodified
146 * repository state, in which case git refuses to stash.
147 * In that case, return a special identifier as "specialprefix:<branch>:<head revision>",
148 * which will trigger a checkout in restore(). */
150 QString GitVersionControl::vcsCreateSnapshot(const QString &topLevel)
152 bool repositoryUnchanged;
153 // Create unique keyword
155 QString keyword = QLatin1String(stashMessageKeywordC) + QString::number(n++);
156 const QString stashMessage =
157 gitClient()->synchronousStash(topLevel, keyword,
158 GitClient::StashImmediateRestore|GitClient::StashIgnoreUnchanged,
159 &repositoryUnchanged);
160 if (!stashMessage.isEmpty())
162 if (repositoryUnchanged) {
163 // For unchanged repository state: return identifier + top revision
166 if (!gitClient()->synchronousTopRevision(topLevel, &topRevision, &branch))
168 const QChar colon = QLatin1Char(':');
169 QString id = QLatin1String(stashRevisionIdC);
176 return QString(); // Failure
179 QStringList GitVersionControl::vcsSnapshots(const QString &topLevel)
181 QList<Stash> stashes;
182 if (!gitClient()->synchronousStashList(topLevel, &stashes))
183 return QStringList();
184 // Return the git stash 'message' as identifier, ignoring empty ones
186 foreach(const Stash &s, stashes)
187 if (!s.message.isEmpty())
188 rc.push_back(s.message);
192 bool GitVersionControl::vcsRestoreSnapshot(const QString &topLevel, const QString &name)
194 bool success = false;
196 // Is this a revision or a stash
197 if (name.startsWith(QLatin1String(stashRevisionIdC))) {
198 // Restore "id:branch:revision"
199 const QStringList tokens = name.split(QLatin1Char(':'));
200 if (tokens.size() != 3)
202 const QString branch = tokens.at(1);
203 const QString revision = tokens.at(2);
204 success = gitClient()->synchronousReset(topLevel)
205 && gitClient()->synchronousCheckoutBranch(topLevel, branch)
206 && gitClient()->synchronousCheckoutFiles(topLevel, QStringList(), revision);
208 // Restore stash if it can be resolved.
210 success = gitClient()->stashNameFromMessage(topLevel, name, &stashName)
211 && gitClient()->synchronousReset(topLevel)
212 && gitClient()->synchronousStashRestore(topLevel, stashName);
218 bool GitVersionControl::vcsRemoveSnapshot(const QString &topLevel, const QString &name)
220 // Is this a revision -> happy
221 if (name.startsWith(QLatin1String(stashRevisionIdC)))
224 return gitClient()->stashNameFromMessage(topLevel, name, &stashName)
225 && gitClient()->synchronousStashRemove(topLevel, stashName);
228 bool GitVersionControl::managesDirectory(const QString &directory, QString *topLevel) const
230 const QString topLevelFound = GitClient::findRepositoryForDirectory(directory);
232 *topLevel = topLevelFound;
233 return !topLevelFound.isEmpty();
236 bool GitVersionControl::vcsAnnotate(const QString &file, int line)
238 const QFileInfo fi(file);
239 gitClient()->blame(fi.absolutePath(), QStringList(), fi.fileName(), QString(), line);
243 void GitVersionControl::emitFilesChanged(const QStringList &l)
245 emit filesChanged(l);
248 void GitVersionControl::emitRepositoryChanged(const QString &r)
250 emit repositoryChanged(r);