Don't show bisect branches in 'vng branch'
[vng.git] / src / Configuration.cpp
blob036d225544322c479c21575d08562b15ea435bb4
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 const bool remotes = name.startsWith(QLatin1String("remotes/"));
301 if (!remotes && !name.startsWith(QLatin1String("heads/")))
302 continue;
304 name = name.trimmed(); // remove linefeed
305 Branch branch(name, QString::fromLatin1(buf, 40));
306 if (name.startsWith(QLatin1String("remotes")))
307 m_remoteBranches.append(branch);
308 else
309 m_localBranches.append(branch);
313 QFile localRef(repositoryMetaDir().absolutePath()+ QLatin1String("/HEAD"));
314 if (localRef.open(QIODevice::ReadOnly)) {
315 qint64 lineLength = Vng::readLine(&localRef, buf, sizeof(buf));
316 if (lineLength >= 1) {
317 QString line = QString::fromLatin1(buf);
318 if (line.startsWith(QLatin1String("ref: refs/"))) {
319 QString head = line.mid(10, line.length()-11);
320 foreach (Branch branch, m_localBranches) {
321 if (branch.branchName() == head) {
322 branch.setIsHead(true);
323 break;
331 void Configuration::pullConfigDataFrom(const Configuration &other)
333 if (other.m_fetchedBranches) {
334 m_localBranches = other.m_localBranches;
335 m_remoteBranches = other.m_remoteBranches;
336 m_fetchedBranches = true;
338 if (other.m_configRead) {
339 m_remoteRepos = other.m_remoteRepos;
340 m_trackedBranches = other.m_trackedBranches;
341 m_configRead = true;
345 QList<TrackedBranch> Configuration::trackedBranches()
347 const_cast<Configuration*> (this)->readConfig();
348 return m_trackedBranches;
351 QList<RemoteRepo> Configuration::remotes()
353 const_cast<Configuration*> (this)->readConfig();
354 return m_remoteRepos;
357 void Configuration::addRepo(const RemoteRepo &newRepo, AddStrategy strategy)
359 RemoteRepo defaultRepo;
360 // check if its there already.
361 foreach (const RemoteRepo &repo, m_remoteRepos) {
362 if (repo.url() == newRepo.url()) {
363 if (strategy == AddNotDefault)
364 return;
365 if (repo.isDefault())
366 return;
367 } else if (repo.isDefault()) {
368 defaultRepo = repo;
371 QProcess git;
372 QStringList arguments;
373 GitRunner runner(git, arguments);
374 if (defaultRepo.isValid() && strategy == AddAsDefault) {
375 // move the default one to a normal url.
376 arguments << QLatin1String("config") << QLatin1String("--add")
377 << QLatin1String("vng.remote.url") << defaultRepo.url();
378 runner.setArguments(arguments);
379 AbstractCommand::ReturnCodes rc = runner.start(GitRunner::WaitUntilFinished);
380 if (rc != AbstractCommand::Ok) {
381 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";
382 return;
385 // store it as default.
386 arguments.clear();
387 arguments << QLatin1String("config");
388 if (strategy == AddAsDefault)
389 arguments << QLatin1String("--replace-all") << QLatin1String("vng.remote.default");
390 else
391 arguments << QLatin1String("--add") << QLatin1String("vng.remote.url");
392 arguments << newRepo.url();
393 runner.setArguments(arguments);
394 AbstractCommand::ReturnCodes rc = runner.start(GitRunner::WaitUntilFinished);
395 if (rc != AbstractCommand::Ok) {
396 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";
397 return;