25302cd5abfbf010e137c305f565942394968966
[vng.git] / src / Configuration.cpp
blob25302cd5abfbf010e137c305f565942394968966
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008-2009 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "Configuration.h"
19 #include "Logger.h"
20 #include "GitRunner.h"
21 #include "AbstractCommand.h"
23 #include <QDebug>
24 #include <QMutexLocker>
25 #include <QProcess>
27 #ifndef Q_OS_WIN
28 #include <unistd.h>
29 #endif
31 Configuration::Configuration(const char *section)
32 : m_repoDir(QLatin1String(".")),
33 m_section(QString::fromLatin1(section)),
34 m_vngConfigRead(false),
35 m_emptyRepo(false),
36 m_fetchedBranches(false),
37 m_configRead(false)
41 bool Configuration::contains(const QString & key) const
43 const_cast<Configuration*> (this)->readVngConfig();
44 return m_options.contains(key);
47 QDir Configuration::repository() const
49 const_cast<Configuration*> (this)->readVngConfig();
50 return m_repoDir;
53 QDir Configuration::repositoryMetaDir() const
55 const_cast<Configuration*> (this)->readVngConfig();
56 return m_repoMetaDataDir;
59 void Configuration::readVngConfig()
61 if (m_vngConfigRead)
62 return;
63 m_vngConfigRead = true;
64 QObject deleterParent;
66 QDir dir = QDir::current();
67 do {
68 QDir git = dir;
69 if (git.cd(QLatin1String(".git"))) {
70 m_repoDir = dir;
71 m_repoMetaDataDir = git;
72 QDir refs(git.absoluteFilePath(QLatin1String("refs/heads")));
73 m_emptyRepo = refs.count() == 2; // only '.' and '..'
74 break;
76 if (!dir.cdUp())
77 break;
78 } while(!dir.isRoot());
80 QString home = QDir::homePath();
81 QFile *config;
82 config = new QFile(home + QLatin1String("/.vng/config"), &deleterParent);
83 if (! config->exists())
84 config = new QFile(home + QLatin1String("/.darcs/defaults"), &deleterParent);
85 if (config->exists()) {
86 if (! config->open(QIODevice::ReadOnly)) {
87 Logger::error() << "Failed to open config file, is it readable?\n";
88 return;
91 char buf[1024];
92 while(true) {
93 qint64 lineLength = config->readLine(buf, sizeof(buf));
94 if (lineLength == -1)
95 break;
96 QString line = QString::fromUtf8(buf, lineLength);
97 QString option;
98 if (line.startsWith(QLatin1String("ALL ")))
99 option = line.mid(3).trimmed();
100 else if (line.length() > m_section.length() && line.startsWith(m_section))
101 option = line.mid(m_section.length()).trimmed();
102 if (! option.isEmpty()) {
103 const int index = option.indexOf(QLatin1Char(' '));
104 if (index > 0)
105 m_options.insert(option.left(index).trimmed(), option.mid(index).trimmed());
106 else
107 m_options.insert(option, QString());
110 config->close();
114 void Configuration::readConfig()
116 if (m_configRead)
117 return;
118 m_configRead = true;
120 readVngConfig();
121 QFile config(m_repoMetaDataDir.absolutePath() + QLatin1String("/config"));
122 if (! config.open(QIODevice::ReadOnly))
123 return;
125 class RepoCreator {
126 public:
127 RepoCreator() : active(false) { }
128 QString name;
129 QString repoUrl;
130 bool active;
132 void create(QList<RemoteRepo> &repos) {
133 if (active && !repoUrl.isEmpty())
134 repos.append(RemoteRepo(name, repoUrl));
135 active = false;
136 name.clear();
137 repoUrl.clear();
140 RepoCreator repoCreator;
141 class BranchCreator {
142 public:
143 BranchCreator() : active(false) { }
144 QString localName;
145 QString remoteName;
146 QString repoName;
147 bool active;
149 void create(QList<TrackedBranch> &branches, const QList<RemoteRepo> &repos) {
150 if (active && !localName.isEmpty() && !repoName.isEmpty()) {
151 foreach (RemoteRepo repo, repos) {
152 if (repo.name() == repoName) {
153 branches.append(TrackedBranch(localName, remoteName, repo));
157 active = false;
158 localName.clear();
159 remoteName.clear();
160 repoName.clear();
163 BranchCreator branchCreator;
165 char bytes[1024];
166 QString defaultRepo;
167 bool vngRemotes = false;
168 while (true) {
169 qint64 lineLength = Vng::readLine(&config, bytes, 1024);
170 if (lineLength == -1)
171 break;
172 char *line = bytes;
173 while (lineLength > 0 && (line[0] == ' ' || line[0] == '\t')) {
174 ++line;
175 --lineLength;
177 if (lineLength == 0 || line[0] == '#' || line[0] == ';')
178 continue;
180 if (line[0] == '[') { // start section
181 repoCreator.create(m_remoteRepos);
182 branchCreator.create(m_trackedBranches, m_remoteRepos);
183 vngRemotes = false;
184 QString sectionLine = QString::fromAscii(line+1, lineLength-1).trimmed();
185 if (sectionLine.startsWith(QLatin1String("remote \""))) {
186 repoCreator.active = true;
187 repoCreator.name = sectionLine.mid(8, sectionLine.length()-10);
188 } else if (sectionLine.startsWith(QLatin1String("branch \""))) {
189 branchCreator.active = true;
190 branchCreator.remoteName = sectionLine.mid(8, sectionLine.length()-10);
191 } else if (sectionLine.startsWith(QLatin1String("vng \"remote\""))) {
192 vngRemotes = true;
195 else if (vngRemotes || repoCreator.active || branchCreator.active) {
196 QString variableLine = QString::fromAscii(line, lineLength-1);
197 int index = variableLine.indexOf(QLatin1Char('='));
198 if (index <= 0)
199 continue;
200 QString variable = variableLine.left(index).trimmed();
201 QString value = variableLine.mid(index+1).trimmed();
202 if (variable == QLatin1String("url") && repoCreator.active) {
203 repoCreator.repoUrl = value;
204 } else if (branchCreator.active && variable == QLatin1String("remote")) {
205 branchCreator.repoName = value;
206 } else if (branchCreator.active && variable == QLatin1String("merge")) {
207 index = value.lastIndexOf(QLatin1Char('/'));
208 if (index > 0 && value.length() > index)
209 branchCreator.localName = value.mid(index+1);
210 } else if (vngRemotes && variable == QLatin1String("default")) {
211 defaultRepo = value;
212 } else if (vngRemotes && variable == QLatin1String("url")) {
213 bool found = false;
214 foreach (const RemoteRepo &repo, m_remoteRepos) {
215 if (repo.url() == value) { // no need to add it twice
216 found = true;
217 break;
220 if (!found)
221 m_remoteRepos.append(RemoteRepo(value, value));
225 repoCreator.create(m_remoteRepos);
226 branchCreator.create(m_trackedBranches, m_remoteRepos);
228 foreach (RemoteRepo repo, m_remoteRepos) {
229 if (repo.url() == defaultRepo) {
230 repo.setIsDefault(true);
231 break;
236 QString Configuration::optionArgument(const QString &optionName, const QString &defaultValue) const
238 if (m_options.contains(optionName))
239 return m_options[optionName];
240 return defaultValue;
243 bool Configuration::colorTerm() const
245 #ifndef Q_OS_WIN
246 if (isatty(1))
247 return QString::fromLatin1(getenv("TERM")) != QLatin1String("dumb") && !Logger::hasNonColorPager();
248 #endif
249 return false;
252 bool Configuration::isEmptyRepo() const
254 const_cast<Configuration*> (this)->readVngConfig();
255 return m_emptyRepo;
258 QList<Branch> Configuration::allBranches()
260 fetchBranches();
261 QList<Branch> answer;
262 answer += m_localBranches;
263 answer += m_remoteBranches;
264 return answer;
267 QList<Branch> Configuration::branches()
269 fetchBranches();
270 return m_localBranches;
273 QList<Branch> Configuration::remoteBranches()
275 fetchBranches();
276 return m_remoteBranches;
279 void Configuration::fetchBranches()
281 if (m_fetchedBranches)
282 return;
283 m_fetchedBranches = true;
285 QProcess git;
286 QStringList arguments;
287 arguments << QLatin1String("ls-remote") << QLatin1String(".");
288 GitRunner runner(git, arguments);
289 AbstractCommand::ReturnCodes rc = runner.start(GitRunner::WaitForStandardOutput);
290 if (rc != AbstractCommand::Ok) {
291 return;
293 char buf[1024];
294 while(true) {
295 qint64 lineLength = Vng::readLine(&git, buf, sizeof(buf));
296 if (lineLength == -1)
297 break;
298 if (lineLength > 46) { // only take stuff that is in the 'refs' dir.
299 QString name = QString::fromUtf8(buf + 46);
300 name = name.trimmed(); // remove linefeed
301 Branch branch(name, QString::fromLatin1(buf, 40));
302 if (name.startsWith(QLatin1String("remotes")))
303 m_remoteBranches.append(branch);
304 else
305 m_localBranches.append(branch);
309 QFile localRef(repositoryMetaDir().absolutePath()+ QLatin1String("/HEAD"));
310 if (localRef.open(QIODevice::ReadOnly)) {
311 qint64 lineLength = Vng::readLine(&localRef, buf, sizeof(buf));
312 if (lineLength >= 1) {
313 QString line = QString::fromLatin1(buf);
314 if (line.startsWith(QLatin1String("ref: refs/"))) {
315 QString head = line.mid(10, line.length()-11);
316 foreach (Branch branch, m_localBranches) {
317 if (branch.branchName() == head) {
318 branch.setIsHead(true);
319 break;
327 void Configuration::pullConfigDataFrom(const Configuration &other)
329 if (other.m_fetchedBranches) {
330 m_localBranches = other.m_localBranches;
331 m_remoteBranches = other.m_remoteBranches;
332 m_fetchedBranches = true;
334 if (other.m_configRead) {
335 m_remoteRepos = other.m_remoteRepos;
336 m_trackedBranches = other.m_trackedBranches;
337 m_configRead = true;
341 QList<TrackedBranch> Configuration::trackedBranches()
343 const_cast<Configuration*> (this)->readConfig();
344 return m_trackedBranches;
347 QList<RemoteRepo> Configuration::remotes()
349 const_cast<Configuration*> (this)->readConfig();
350 return m_remoteRepos;
353 void Configuration::addRepo(const RemoteRepo &newRepo, AddStrategy strategy)
355 RemoteRepo defaultRepo;
356 // check if its there already.
357 foreach (const RemoteRepo &repo, m_remoteRepos) {
358 if (repo.url() == newRepo.url()) {
359 if (strategy == AddNotDefault)
360 return;
361 if (repo.isDefault())
362 return;
363 } else if (repo.isDefault()) {
364 defaultRepo = repo;
367 QProcess git;
368 QStringList arguments;
369 GitRunner runner(git, arguments);
370 if (defaultRepo.isValid() && strategy == AddAsDefault) {
371 // move the default one to a normal url.
372 arguments << QLatin1String("config") << QLatin1String("--add")
373 << QLatin1String("vng.remote.url") << defaultRepo.url();
374 runner.setArguments(arguments);
375 AbstractCommand::ReturnCodes rc = runner.start(GitRunner::WaitUntilFinished);
376 if (rc != AbstractCommand::Ok) {
377 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";
378 return;
381 // store it as default.
382 arguments.clear();
383 arguments << QLatin1String("config");
384 if (strategy == AddAsDefault)
385 arguments << QLatin1String("--replace-all") << QLatin1String("vng.remote.default");
386 else
387 arguments << QLatin1String("--add") << QLatin1String("vng.remote.url");
388 arguments << newRepo.url();
389 runner.setArguments(arguments);
390 AbstractCommand::ReturnCodes rc = runner.start(GitRunner::WaitUntilFinished);
391 if (rc != AbstractCommand::Ok) {
392 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";
393 return;