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 '..'
75 QFile
refs(git
.absolutePath() + QLatin1String("/packed-refs"));
76 m_emptyRepo
= !refs
.exists();
82 } while(!dir
.isRoot());
84 QString home
= QDir::homePath();
86 config
= new QFile(home
+ QLatin1String("/.vng/config"), &deleterParent
);
87 if (! config
->exists())
88 config
= new QFile(home
+ QLatin1String("/.darcs/defaults"), &deleterParent
);
89 if (config
->exists()) {
90 if (! config
->open(QIODevice::ReadOnly
)) {
91 Logger::error() << "Failed to open config file, is it readable?\n";
97 qint64 lineLength
= config
->readLine(buf
, sizeof(buf
));
100 QString line
= QString::fromUtf8(buf
, lineLength
);
102 if (line
.startsWith(QLatin1String("ALL ")))
103 option
= line
.mid(3).trimmed();
104 else if (line
.length() > m_section
.length() && line
.startsWith(m_section
))
105 option
= line
.mid(m_section
.length()).trimmed();
106 if (! option
.isEmpty()) {
107 const int index
= option
.indexOf(QLatin1Char(' '));
109 m_options
.insert(option
.left(index
).trimmed(), option
.mid(index
).trimmed());
111 m_options
.insert(option
, QString());
118 void Configuration::readConfig()
125 QFile
config(m_repoMetaDataDir
.absolutePath() + QLatin1String("/config"));
126 if (! config
.open(QIODevice::ReadOnly
))
131 RepoCreator() : active(false) { }
136 void create(QList
<RemoteRepo
> &repos
) {
137 if (active
&& !repoUrl
.isEmpty())
138 repos
.append(RemoteRepo(name
, repoUrl
));
144 RepoCreator repoCreator
;
145 class BranchCreator
{
147 BranchCreator() : active(false) { }
153 void create(QList
<TrackedBranch
> &branches
, const QList
<RemoteRepo
> &repos
) {
154 if (active
&& !localName
.isEmpty() && !repoName
.isEmpty()) {
155 foreach (RemoteRepo repo
, repos
) {
156 if (repo
.name() == repoName
) {
157 branches
.append(TrackedBranch(localName
, remoteName
, repo
));
167 BranchCreator branchCreator
;
171 bool vngRemotes
= false;
173 qint64 lineLength
= Vng::readLine(&config
, bytes
, 1024);
174 if (lineLength
== -1)
177 while (lineLength
> 0 && (line
[0] == ' ' || line
[0] == '\t')) {
181 if (lineLength
== 0 || line
[0] == '#' || line
[0] == ';')
184 if (line
[0] == '[') { // start section
185 repoCreator
.create(m_remoteRepos
);
186 branchCreator
.create(m_trackedBranches
, m_remoteRepos
);
188 QString sectionLine
= QString::fromAscii(line
+1, lineLength
-1).trimmed();
189 if (sectionLine
.startsWith(QLatin1String("remote \""))) {
190 repoCreator
.active
= true;
191 repoCreator
.name
= sectionLine
.mid(8, sectionLine
.length()-10);
192 } else if (sectionLine
.startsWith(QLatin1String("branch \""))) {
193 branchCreator
.active
= true;
194 branchCreator
.remoteName
= sectionLine
.mid(8, sectionLine
.length()-10);
195 } else if (sectionLine
.startsWith(QLatin1String("vng \"remote\""))) {
199 else if (vngRemotes
|| repoCreator
.active
|| branchCreator
.active
) {
200 QString variableLine
= QString::fromAscii(line
, lineLength
-1);
201 int index
= variableLine
.indexOf(QLatin1Char('='));
204 QString variable
= variableLine
.left(index
).trimmed();
205 QString value
= variableLine
.mid(index
+1).trimmed();
206 if (variable
== QLatin1String("url") && repoCreator
.active
) {
207 repoCreator
.repoUrl
= value
;
208 } else if (branchCreator
.active
&& variable
== QLatin1String("remote")) {
209 branchCreator
.repoName
= value
;
210 } else if (branchCreator
.active
&& variable
== QLatin1String("merge")) {
211 index
= value
.lastIndexOf(QLatin1Char('/'));
212 if (index
> 0 && value
.length() > index
)
213 branchCreator
.localName
= value
.mid(index
+1);
214 } else if (vngRemotes
&& variable
== QLatin1String("default")) {
216 } else if (vngRemotes
&& variable
== QLatin1String("url")) {
218 foreach (const RemoteRepo
&repo
, m_remoteRepos
) {
219 if (repo
.url() == value
) { // no need to add it twice
225 m_remoteRepos
.append(RemoteRepo(value
, value
));
229 repoCreator
.create(m_remoteRepos
);
230 branchCreator
.create(m_trackedBranches
, m_remoteRepos
);
232 foreach (RemoteRepo repo
, m_remoteRepos
) {
233 if (repo
.url() == defaultRepo
) {
234 repo
.setIsDefault(true);
240 QString
Configuration::optionArgument(const QString
&optionName
, const QString
&defaultValue
) const
242 if (m_options
.contains(optionName
))
243 return m_options
[optionName
];
247 bool Configuration::colorTerm() const
251 return QString::fromLatin1(getenv("TERM")) != QLatin1String("dumb") && !Logger::hasNonColorPager();
256 bool Configuration::isEmptyRepo() const
258 const_cast<Configuration
*> (this)->readVngConfig();
262 QList
<Branch
> Configuration::allBranches()
265 QList
<Branch
> answer
;
266 answer
+= m_localBranches
;
267 answer
+= m_remoteBranches
;
271 QList
<Branch
> Configuration::branches()
274 return m_localBranches
;
277 QList
<Branch
> Configuration::remoteBranches()
280 return m_remoteBranches
;
283 void Configuration::fetchBranches()
285 if (m_fetchedBranches
)
287 m_fetchedBranches
= true;
290 QStringList arguments
;
291 arguments
<< QLatin1String("ls-remote") << QLatin1String(".");
292 GitRunner
runner(git
, arguments
);
293 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
294 if (rc
!= AbstractCommand::Ok
) {
299 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
300 if (lineLength
== -1)
302 if (lineLength
> 46) { // only take stuff that is in the 'refs' dir.
303 QString name
= QString::fromUtf8(buf
+ 46);
304 const bool remotes
= name
.startsWith(QLatin1String("remotes/"));
305 if (!remotes
&& !name
.startsWith(QLatin1String("heads/")))
308 name
= name
.trimmed(); // remove linefeed
309 Branch
branch(name
, QString::fromLatin1(buf
, 40));
310 if (name
.startsWith(QLatin1String("remotes")))
311 m_remoteBranches
.append(branch
);
313 m_localBranches
.append(branch
);
317 QFile
localRef(repositoryMetaDir().absolutePath()+ QLatin1String("/HEAD"));
318 if (localRef
.open(QIODevice::ReadOnly
)) {
319 qint64 lineLength
= Vng::readLine(&localRef
, buf
, sizeof(buf
));
320 if (lineLength
>= 1) {
321 QString line
= QString::fromLatin1(buf
);
322 if (line
.startsWith(QLatin1String("ref: refs/"))) {
323 QString head
= line
.mid(10, line
.length()-11);
324 foreach (Branch branch
, m_localBranches
) {
325 if (branch
.branchName() == head
) {
326 branch
.setIsHead(true);
335 void Configuration::pullConfigDataFrom(const Configuration
&other
)
337 if (other
.m_fetchedBranches
) {
338 m_localBranches
= other
.m_localBranches
;
339 m_remoteBranches
= other
.m_remoteBranches
;
340 m_fetchedBranches
= true;
342 if (other
.m_configRead
) {
343 m_remoteRepos
= other
.m_remoteRepos
;
344 m_trackedBranches
= other
.m_trackedBranches
;
349 QList
<TrackedBranch
> Configuration::trackedBranches()
351 const_cast<Configuration
*> (this)->readConfig();
352 return m_trackedBranches
;
355 QList
<RemoteRepo
> Configuration::remotes()
357 const_cast<Configuration
*> (this)->readConfig();
358 return m_remoteRepos
;
361 void Configuration::addRepo(const RemoteRepo
&newRepo
, AddStrategy strategy
)
363 RemoteRepo defaultRepo
;
364 // check if its there already.
365 foreach (const RemoteRepo
&repo
, m_remoteRepos
) {
366 if (repo
.url() == newRepo
.url()) {
367 if (strategy
== AddNotDefault
)
369 if (repo
.isDefault())
371 } else if (repo
.isDefault()) {
376 QStringList arguments
;
377 GitRunner
runner(git
, arguments
);
378 if (defaultRepo
.isValid() && strategy
== AddAsDefault
) {
379 // move the default one to a normal url.
380 arguments
<< QLatin1String("config") << QLatin1String("--add")
381 << QLatin1String("vng.remote.url") << defaultRepo
.url();
382 runner
.setArguments(arguments
);
383 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitUntilFinished
);
384 if (rc
!= AbstractCommand::Ok
) {
385 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";
389 // store it as default.
391 arguments
<< QLatin1String("config");
392 if (strategy
== AddAsDefault
)
393 arguments
<< QLatin1String("--replace-all") << QLatin1String("vng.remote.default");
395 arguments
<< QLatin1String("--add") << QLatin1String("vng.remote.url");
396 arguments
<< newRepo
.url();
397 runner
.setArguments(arguments
);
398 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitUntilFinished
);
399 if (rc
!= AbstractCommand::Ok
) {
400 Logger::warn() << "Failed to store remote repository in local config; check permissions\n";