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"
20 #include "GitRunner.h"
21 #include "AbstractCommand.h"
24 #include <QMutexLocker>
31 Configuration::Configuration(const char *section
)
32 : m_repoDir(QLatin1String(".")),
33 m_section(QString::fromLatin1(section
)),
34 m_vngConfigRead(false),
36 m_fetchedBranches(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();
53 QDir
Configuration::repositoryMetaDir() const
55 const_cast<Configuration
*> (this)->readVngConfig();
56 return m_repoMetaDataDir
;
59 void Configuration::readVngConfig()
63 m_vngConfigRead
= true;
64 QObject deleterParent
;
66 QDir dir
= QDir::current();
69 if (git
.cd(QLatin1String(".git"))) {
71 m_repoMetaDataDir
= git
;
72 QDir
refs(git
.absoluteFilePath(QLatin1String("refs/heads")));
73 m_emptyRepo
= refs
.count() == 2; // only '.' and '..'
78 } while(!dir
.isRoot());
80 QString home
= QDir::homePath();
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";
93 qint64 lineLength
= config
->readLine(buf
, sizeof(buf
));
96 QString line
= QString::fromUtf8(buf
, lineLength
);
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(' '));
105 m_options
.insert(option
.left(index
).trimmed(), option
.mid(index
).trimmed());
107 m_options
.insert(option
, QString());
114 void Configuration::readConfig()
121 QFile
config(m_repoMetaDataDir
.absolutePath() + QLatin1String("/config"));
122 if (! config
.open(QIODevice::ReadOnly
))
127 RepoCreator() : active(false) { }
132 void create(QList
<RemoteRepo
> &repos
) {
133 if (active
&& !repoUrl
.isEmpty())
134 repos
.append(RemoteRepo(name
, repoUrl
));
140 RepoCreator repoCreator
;
141 class BranchCreator
{
143 BranchCreator() : active(false) { }
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
));
163 BranchCreator branchCreator
;
167 bool vngRemotes
= false;
169 qint64 lineLength
= Vng::readLine(&config
, bytes
, 1024);
170 if (lineLength
== -1)
173 while (lineLength
> 0 && (line
[0] == ' ' || line
[0] == '\t')) {
177 if (lineLength
== 0 || line
[0] == '#' || line
[0] == ';')
180 if (line
[0] == '[') { // start section
181 repoCreator
.create(m_remoteRepos
);
182 branchCreator
.create(m_trackedBranches
, m_remoteRepos
);
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\""))) {
195 else if (vngRemotes
|| repoCreator
.active
|| branchCreator
.active
) {
196 QString variableLine
= QString::fromAscii(line
, lineLength
-1);
197 int index
= variableLine
.indexOf(QLatin1Char('='));
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")) {
212 } else if (vngRemotes
&& variable
== QLatin1String("url")) {
214 foreach (const RemoteRepo
&repo
, m_remoteRepos
) {
215 if (repo
.url() == value
) { // no need to add it twice
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);
236 QString
Configuration::optionArgument(const QString
&optionName
, const QString
&defaultValue
) const
238 if (m_options
.contains(optionName
))
239 return m_options
[optionName
];
243 bool Configuration::colorTerm() const
247 return QString::fromLatin1(getenv("TERM")) != QLatin1String("dumb") && !Logger::hasNonColorPager();
252 bool Configuration::isEmptyRepo() const
254 const_cast<Configuration
*> (this)->readVngConfig();
258 QList
<Branch
> Configuration::allBranches()
261 QList
<Branch
> answer
;
262 answer
+= m_localBranches
;
263 answer
+= m_remoteBranches
;
267 QList
<Branch
> Configuration::branches()
270 return m_localBranches
;
273 QList
<Branch
> Configuration::remoteBranches()
276 return m_remoteBranches
;
279 void Configuration::fetchBranches()
281 if (m_fetchedBranches
)
283 m_fetchedBranches
= true;
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
) {
295 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
296 if (lineLength
== -1)
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
);
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);
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
;
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
)
361 if (repo
.isDefault())
363 } else if (repo
.isDefault()) {
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";
381 // store it as default.
383 arguments
<< QLatin1String("config");
384 if (strategy
== AddAsDefault
)
385 arguments
<< QLatin1String("--replace-all") << QLatin1String("vng.remote.default");
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";