OSDN Git Service

c68b6c9a4c95f1313c521fee7768d04c4e978ca0
[qt-creator-jp/qt-creator-jp.git] / src / plugins / git / gitversioncontrol.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "gitversioncontrol.h"
35 #include "gitclient.h"
36 #include "gitplugin.h"
37 #include "gitutils.h"
38
39 #include <utils/qtcassert.h>
40
41 #include <QtCore/QDebug>
42 #include <QtCore/QFileInfo>
43
44 static const char stashMessageKeywordC[] = "IVersionControl@";
45 static const char stashRevisionIdC[] = "revision";
46
47 namespace Git {
48 namespace Internal {
49
50 static inline GitClient *gitClient()
51 {
52     return GitPlugin::instance()->gitClient();
53 }
54
55 GitVersionControl::GitVersionControl(GitClient *client) :
56     m_enabled(true),
57     m_client(client)
58 {
59 }
60
61 QString GitVersionControl::displayName() const
62 {
63     return QLatin1String("git");
64 }
65
66 // Add: Implement using "git add --intent-to-add" starting from 1.6.1
67 static inline bool addOperationSupported()
68 {
69     return gitClient()->gitVersion(true) >= version(1, 6, 1);
70 }
71
72 bool GitVersionControl::supportsOperation(Operation operation) const
73 {
74     bool rc = false;
75     switch (operation) {
76     case AddOperation:
77         rc = addOperationSupported();
78         break;
79     case DeleteOperation:
80         rc = true;
81         break;
82     case MoveOperation:
83         rc = true;
84         break;
85     case OpenOperation:
86         break;
87     case CreateRepositoryOperation:
88     case SnapshotOperations:
89         rc = true;
90         break;
91     case AnnotateOperation:
92         rc = true;
93         break;
94     case CheckoutOperation:
95     case GetRepositoryRootOperation:
96         rc = true;
97         break;
98     }
99     return rc;
100 }
101
102 bool GitVersionControl::vcsOpen(const QString & /*fileName*/)
103 {
104     return false;
105 }
106
107 bool GitVersionControl::vcsAdd(const QString & fileName)
108 {
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()));
113 }
114
115 bool GitVersionControl::vcsDelete(const QString & fileName)
116 {
117     const QFileInfo fi(fileName);
118     return gitClient()->synchronousDelete(fi.absolutePath(), true, QStringList(fi.fileName()));
119 }
120
121 bool GitVersionControl::vcsMove(const QString &from, const QString &to)
122 {
123     const QFileInfo fromInfo(from);
124     const QFileInfo toInfo(to);
125     return gitClient()->synchronousMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
126 }
127
128 bool GitVersionControl::vcsCreateRepository(const QString &directory)
129 {
130     return gitClient()->synchronousInit(directory);
131 }
132
133 bool GitVersionControl::vcsCheckout(const QString &directory, const QByteArray &url)
134 {
135     return gitClient()->cloneRepository(directory,url);
136 }
137
138 QString GitVersionControl::vcsGetRepositoryURL(const QString &directory)
139 {
140     return gitClient()->vcsGetRepositoryURL(directory);
141 }
142
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(). */
149
150 QString GitVersionControl::vcsCreateSnapshot(const QString &topLevel)
151 {
152     bool repositoryUnchanged;
153     // Create unique keyword
154     static int n = 1;
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())
161         return stashMessage;
162     if (repositoryUnchanged) {
163         // For unchanged repository state: return identifier + top revision
164         QString topRevision;
165         QString branch;
166         if (!gitClient()->synchronousTopRevision(topLevel, &topRevision, &branch))
167             return QString();
168         const QChar colon = QLatin1Char(':');
169         QString id = QLatin1String(stashRevisionIdC);
170         id += colon;
171         id += branch;
172         id += colon;
173         id += topRevision;
174         return id;
175     }
176     return QString(); // Failure
177 }
178
179 QStringList GitVersionControl::vcsSnapshots(const QString &topLevel)
180 {
181     QList<Stash> stashes;
182     if (!gitClient()->synchronousStashList(topLevel, &stashes))
183         return QStringList();
184     // Return the git stash 'message' as identifier, ignoring empty ones
185     QStringList rc;
186     foreach(const Stash &s, stashes)
187         if (!s.message.isEmpty())
188             rc.push_back(s.message);
189     return rc;
190 }
191
192 bool GitVersionControl::vcsRestoreSnapshot(const QString &topLevel, const QString &name)
193 {
194     bool success = false;
195     do {
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)
201                 break;
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);
207         } else {
208             // Restore stash if it can be resolved.
209             QString stashName;
210             success = gitClient()->stashNameFromMessage(topLevel, name, &stashName)
211                       && gitClient()->synchronousReset(topLevel)
212                       && gitClient()->synchronousStashRestore(topLevel, stashName);
213         }
214     }  while (false);
215     return success;
216 }
217
218 bool GitVersionControl::vcsRemoveSnapshot(const QString &topLevel, const QString &name)
219 {
220     // Is this a revision -> happy
221     if (name.startsWith(QLatin1String(stashRevisionIdC)))
222         return true;
223     QString stashName;
224     return gitClient()->stashNameFromMessage(topLevel, name, &stashName)
225             && gitClient()->synchronousStashRemove(topLevel, stashName);
226 }
227
228 bool GitVersionControl::managesDirectory(const QString &directory, QString *topLevel) const
229 {
230     const QString topLevelFound = GitClient::findRepositoryForDirectory(directory);
231     if (topLevel)
232         *topLevel = topLevelFound;
233     return !topLevelFound.isEmpty();
234 }
235
236 bool GitVersionControl::vcsAnnotate(const QString &file, int line)
237 {
238     const QFileInfo fi(file);
239     gitClient()->blame(fi.absolutePath(), QStringList(), fi.fileName(), QString(), line);
240     return true;
241 }
242
243 void GitVersionControl::emitFilesChanged(const QStringList &l)
244 {
245     emit filesChanged(l);
246 }
247
248 void GitVersionControl::emitRepositoryChanged(const QString &r)
249 {
250     emit repositoryChanged(r);
251 }
252
253 } // Internal
254 } // Git