OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / git / commitdata.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 "commitdata.h"
35 #include <utils/qtcassert.h>
36
37 #include <QtCore/QDebug>
38 #include <QtCore/QRegExp>
39
40 const char *const kBranchIndicatorC = "# On branch";
41
42 namespace Git {
43 namespace Internal {
44
45 void GitSubmitEditorPanelInfo::clear()
46 {
47     repository.clear();
48     description.clear();
49     branch.clear();
50 }
51
52 QDebug operator<<(QDebug d, const GitSubmitEditorPanelInfo &data)
53 {
54     d.nospace() << "Rep: " << data.repository << " Descr: " << data.description
55         << " branch: " << data.branch;
56     return d;
57 }
58
59 void GitSubmitEditorPanelData::clear()
60 {
61     author.clear();
62     email.clear();
63 }
64
65 QString GitSubmitEditorPanelData::authorString() const
66 {
67     QString rc;
68     rc += author;
69
70     if (email.isEmpty())
71         return rc;
72
73     rc += QLatin1String(" <");
74     rc += email;
75     rc += QLatin1Char('>');
76     return rc;
77 }
78
79 QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &data)
80 {
81     d.nospace() << " author:" << data.author << " email: " << data.email;
82     return d;
83 }
84
85 void CommitData::clear()
86 {
87     panelInfo.clear();
88     panelData.clear();
89     amendSHA1.clear();
90
91     stagedFiles.clear();
92     unstagedFiles.clear();
93     untrackedFiles.clear();
94 }
95
96 // Split a state/file spec from git status output
97 // '#<tab>modified:<blanks>git .pro'
98 // into state and file ('modified', 'git .pro').
99 CommitData::StateFilePair splitStateFileSpecification(const QString &line)
100 {
101     QPair<QString, QString> rc;
102     const int statePos = 2;
103     const int colonIndex = line.indexOf(QLatin1Char(':'), statePos);
104     if (colonIndex == -1)
105         return rc;
106     rc.first = line.mid(statePos, colonIndex - statePos);
107     int filePos = colonIndex + 1;
108     const QChar blank = QLatin1Char(' ');
109     while (line.at(filePos) == blank)
110         filePos++;
111     if (filePos < line.size())
112         rc.second = line.mid(filePos, line.size() - filePos);
113     return rc;
114 }
115
116 // Convenience to add a state/file spec to a list
117 static inline bool addStateFileSpecification(const QString &line, QList<CommitData::StateFilePair> *list)
118 {
119     const CommitData::StateFilePair sf = splitStateFileSpecification(line);
120     if (sf.first.isEmpty() || sf.second.isEmpty())
121         return false;
122     list->push_back(sf);
123     return true;
124 }
125
126 /* Parse a git status file list:
127  * \code
128     # Changes to be committed:
129     #<tab>modified:<blanks>git.pro
130     # Changed but not updated:
131     #<tab>modified:<blanks>git.pro
132     # Untracked files:
133     #<tab>git.pro
134     \endcode
135 */
136
137 bool CommitData::filesEmpty() const
138 {
139     return stagedFiles.empty() && unstagedFiles.empty() && untrackedFiles.empty();
140 }
141
142 bool CommitData::parseFilesFromStatus(const QString &output)
143 {
144     enum State { None, CommitFiles, NotUpdatedFiles, UntrackedFiles };
145
146     const QStringList lines = output.split(QLatin1Char('\n'));
147     const QString branchIndicator = QLatin1String(kBranchIndicatorC);
148     const QString commitIndicator = QLatin1String("# Changes to be committed:");
149     const QString notUpdatedIndicator = QLatin1String("# Changed but not updated:");
150     const QString untrackedIndicator = QLatin1String("# Untracked files:");
151
152     State s = None;
153     // Match added/changed-not-updated files: "#<tab>modified: foo.cpp"
154     QRegExp filesPattern(QLatin1String("#\\t[^:]+:\\s+.+"));
155     QTC_ASSERT(filesPattern.isValid(), return false);
156
157     const QStringList::const_iterator cend = lines.constEnd();
158     for (QStringList::const_iterator it =  lines.constBegin(); it != cend; ++it) {
159         QString line = *it;
160         if (line.startsWith(branchIndicator)) {
161             panelInfo.branch = line.mid(branchIndicator.size() + 1);
162             continue;
163         }
164         if (line.startsWith(commitIndicator)) {
165             s = CommitFiles;
166             continue;
167         }
168         if (line.startsWith(notUpdatedIndicator)) {
169             s = NotUpdatedFiles;
170             continue;
171         }
172         if (line.startsWith(untrackedIndicator)) {
173             // Now match untracked: "#<tab>foo.cpp"
174             s = UntrackedFiles;
175             filesPattern = QRegExp(QLatin1String("#\\t.+"));
176             QTC_ASSERT(filesPattern.isValid(), return false);
177             continue;
178         }
179         if (filesPattern.exactMatch(line)) {
180             switch (s) {
181             case CommitFiles:
182                 addStateFileSpecification(line, &stagedFiles);
183                 break;
184             case NotUpdatedFiles:
185                 // skip submodules:
186                 if (line.endsWith(QLatin1String(" (modified content)"))
187                         || line.endsWith(" (new commits)"))
188                     line = line.left(line.lastIndexOf(QLatin1Char('(')) - 1);
189                 addStateFileSpecification(line, &unstagedFiles);
190                 break;
191             case UntrackedFiles:
192                 untrackedFiles.push_back(line.mid(2).trimmed());
193                 break;
194             case None:
195                 break;
196             }
197         }
198     }
199     return true;
200 }
201
202 // Convert a spec pair list to a list of file names, optionally
203 // filter for a state
204 static QStringList specToFileNames(const QList<CommitData::StateFilePair> &files,
205                                    const QString &stateFilter)
206 {
207     typedef QList<CommitData::StateFilePair>::const_iterator ConstIterator;
208     if (files.empty())
209         return QStringList();
210     const bool emptyFilter = stateFilter.isEmpty();
211     QStringList rc;
212     const ConstIterator cend = files.constEnd();
213     for (ConstIterator it = files.constBegin(); it != cend; ++it)
214         if (emptyFilter || stateFilter == it->first)
215             rc.push_back(it->second);
216     return rc;
217 }
218
219 QStringList CommitData::stagedFileNames(const QString &stateFilter) const
220 {
221     return specToFileNames(stagedFiles, stateFilter);
222 }
223
224 QStringList CommitData::unstagedFileNames(const QString &stateFilter) const
225 {
226     return specToFileNames(unstagedFiles, stateFilter);
227 }
228
229 QDebug operator<<(QDebug d, const CommitData &data)
230 {
231     d <<  data.panelInfo << data.panelData;
232     d.nospace() << "Commit: " << data.stagedFiles << " Not updated: "
233         << data.unstagedFiles << " Untracked: " << data.untrackedFiles;
234     return d;
235 }
236
237 } // namespace Internal
238 } // namespace Git