2 * This file is part of the vng project
3 * Copyright (C) 2008 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/>.
20 #include "ChangeSet.h"
21 #include "../GitRunner.h"
22 #include "../AbstractCommand.h"
29 Private() : ref(1), renameAcceptance(Vng::Undecided
), protectionAcceptance(Vng::Undecided
), isBinaryFile(false)
33 QString protection
, oldProtection
;
34 QString sha1
, oldSha1
;
36 QByteArray oldFilename
;
39 Vng::Acceptance renameAcceptance
;
40 Vng::Acceptance protectionAcceptance
;
49 File::File(const File
&other
)
61 void File::addHunk(const Hunk
&hunk
)
67 QList
<Hunk
> File::hunks() const
72 bool File::isValid() const
74 return !d
->filename
.isEmpty();
77 void File::setFileName(const QByteArray
&filename
)
79 d
->filename
= filename
;
80 d
->renameAcceptance
= d
->oldFilename
== d
->filename
? Vng::Accepted
: Vng::Undecided
;
83 QByteArray
File::fileName() const
88 File
& File::operator=(const File
&other
)
97 bool File::operator==(const File
&other
)
102 int File::count() const
104 return d
->hunks
.count();
107 void File::setOldFileName(const QByteArray
&filename
)
109 d
->oldFilename
= filename
;
110 d
->renameAcceptance
= d
->oldFilename
== d
->filename
? Vng::Accepted
: Vng::Undecided
;
113 QByteArray
File::oldFileName() const
115 return d
->oldFilename
;
118 void File::setProtection(const QString
&string
)
120 d
->protection
= string
;
121 d
->protectionAcceptance
= string
== d
->oldProtection
? Vng::Accepted
: Vng::Undecided
;
124 void File::setOldProtection(const QString
&string
)
126 d
->oldProtection
= string
;
127 d
->protectionAcceptance
= string
== d
->protection
? Vng::Accepted
: Vng::Undecided
;
130 QString
File::protection() const
132 return d
->protection
;
135 QString
File::oldProtection() const
137 return d
->oldProtection
;
141 void File::setOldSha1(const QString
&string
)
146 void File::setSha1(const QString
&string
)
151 QString
File::oldSha1() const
156 QString
File::sha1() const
161 bool File::hasChanged() const
163 return count() || d
->protection
!= d
->oldProtection
||
164 d
->filename
!= d
->oldFilename
;
167 void File::setRenameAcceptance(Vng::Acceptance accepted
)
169 if (d
->filename
!= d
->oldFilename
)
170 d
->renameAcceptance
= accepted
;
173 void File::setProtectionAcceptance(Vng::Acceptance accepted
)
175 if (d
->protection
!= d
->oldProtection
)
176 d
->protectionAcceptance
= accepted
;
179 Vng::Acceptance
File::renameAcceptance() const
181 return d
->renameAcceptance
;
184 Vng::Acceptance
File::protectionAcceptance() const
186 return d
->protectionAcceptance
;
189 QFile::Permissions
File::permissions() const
191 QFile::Permissions perms
;
192 if (d
->protection
.length() != 6)
194 Q_ASSERT(d
->protection
.length() == 6);
195 const int user
= d
->protection
.mid(3,1).toInt();
196 if (user
& 1) perms
|= QFile::ExeOwner
;
197 if (user
& 2) perms
|= QFile::WriteOwner
;
198 if (user
& 4) perms
|= QFile::ReadOwner
;
199 const int group
= d
->protection
.mid(4,1).toInt();
200 if (group
& 1) perms
|= QFile::ExeGroup
;
201 if (group
& 2) perms
|= QFile::WriteGroup
;
202 if (group
& 4) perms
|= QFile::ReadGroup
;
203 const int other
= d
->protection
.mid(5,1).toInt();
204 if (other
& 1) perms
|= QFile::ExeOther
;
205 if (other
& 2) perms
|= QFile::WriteOther
;
206 if (other
& 4) perms
|= QFile::ReadOther
;
210 int File::linesAdded() const
213 foreach(Hunk hunk
, d
->hunks
)
214 total
+= hunk
.linesAdded();
218 int File::linesRemoved() const
221 foreach(Hunk hunk
, d
->hunks
)
222 total
+= hunk
.linesRemoved();
226 void File::setBinary(bool binary
)
228 d
->isBinaryFile
= binary
;
231 bool File::isBinary() const
233 return d
->isBinaryFile
;
236 void File::fetchHunks(bool againstHead
)
238 if (d
->hunks
.count())
240 if (d
->oldSha1
== d
->sha1
)
241 return; // no change.
245 DiffCreator(File file
) : m_file(file
) { }
246 void wholeFileAsDiff(bool added
)
249 QStringList arguments
;
250 arguments
<< "cat-file" << "blob";
252 arguments
<< m_file
.sha1();
254 arguments
<< m_file
.oldSha1();
255 GitRunner
runner(git
, arguments
);
256 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
257 const char * prefix
= (added
? "+" : "-");
258 if (rc
== AbstractCommand::Ok
) {
260 Hunk hunk
; // its one hunk.
261 QByteArray
header("@@ -1,0 +1,0 @@", 15);
262 hunk
.addLine(header
);
264 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
265 if (lineLength
== -1)
267 QByteArray
array(prefix
, 1);
271 m_file
.addHunk(hunk
);
279 if (fileName().isEmpty()) { // deleted file.
280 DiffCreator
dc(*this);
281 dc
.wholeFileAsDiff(false);
285 QStringList arguments
;
286 if (sha1().startsWith("0000000") || oldSha1().startsWith("0000000")) {
287 // if this is changed without ever being added to the index, sha1() is 000.
288 // if added as a new file oldSha1() is 000
290 arguments
<< "diff-index" << "-p" << "HEAD" << fileName();
292 DiffCreator
dc(*this);
293 dc
.wholeFileAsDiff(true);
298 arguments
<< "diff" << oldSha1() << sha1();
300 GitRunner
runner(git
, arguments
);
301 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
304 ChangeSet::readGitDiff(git
, this);
307 void File::outputWhatsChanged(QTextStream
&out
, Configuration
&config
, bool printSummary
, bool unified
)
309 const bool deleted
= d
->filename
.isEmpty() && !d
->oldFilename
.isEmpty();
310 const bool added
= !deleted
&& !d
->filename
.isEmpty() && d
->oldFilename
.isEmpty();
311 const bool renamed
= !deleted
&& !added
&& d
->filename
!= d
->oldFilename
;
322 if (deleted
|| renamed
)
323 out
<< d
->oldFilename
;
327 out
<< " => " << d
->filename
;
329 int removed
= linesRemoved();
330 int added
= linesAdded();
332 out
<< " -" << QString::number(removed
);
334 out
<< " +" << QString::number(added
);
339 if (deleted
) { // file deleted.
341 config
.colorize(out
);
343 config
.normalColor(out
);
344 out
<< d
->oldFilename
<< endl
;
347 else if (added
) { // file added.
349 config
.colorize(out
);
351 config
.normalColor(out
);
352 out
<< d
->filename
<< endl
;
356 if(renamed
) { // rename
357 if (!unified
) config
.colorize(out
);
359 if (!unified
) config
.normalColor(out
);
360 out
<< "`" << d
->oldFilename
<< "' `" << d
->filename
<< "'" << endl
;
362 if (oldProtection() != protection()) {
363 if (!unified
) config
.colorize(out
);
364 out
<<"mode change ";
365 if (!unified
) config
.normalColor(out
);
366 out
<< d
->filename
<< " " << oldProtection() << " => " << protection() << endl
;
369 if (isBinary()) { // will not have any hunks
370 if (!unified
) config
.colorize(out
);
371 out
<< "binary modification ";
372 if (!unified
) config
.normalColor(out
);
373 out
<< d
->filename
<< endl
;
378 if (d
->oldFilename
.isEmpty())
379 out
<< "/dev/null\n";
381 out
<< d
->oldFilename
<< endl
;
383 if (d
->filename
.isEmpty())
384 out
<< "/dev/null\n";
386 out
<< d
->filename
<< endl
;
388 foreach(Hunk hunk
, hunks()) {
390 out
<< QString(hunk
.header());
391 out
<< QString(hunk
.patch());
394 for (int i
= 0; i
< hunk
.subHunkCount(); i
++) {
395 config
.colorize(out
);
397 QByteArray patch
= hunk
.subHunk(i
);
398 config
.normalColor(out
);
399 out
<< d
->filename
<<" "<< QString::number(hunk
.lineNumber(i
)) << endl
;
400 if (patch
.contains((char) 0)) { // binary
401 config
.colorize(out
);
402 out
<< "binary data\n";
403 config
.normalColor(out
);
406 QString
string (patch
);
407 const bool trailingLinefeed
= string
.length() > 1 && string
[string
.length()-1].unicode() == '\n';
408 if (config
.colorTerm() && trailingLinefeed
)
409 string
= string
.left(string
.length()-1);
411 if (config
.colorTerm() && (!trailingLinefeed
|| string
[string
.length()-1].isSpace())) {
412 config
.colorize2(out
);
414 config
.normalColor(out
);
424 bool File::fileKnownToGit(const QString
&path
)
426 return fileKnownToGit(QFileInfo(path
));
430 bool File::fileKnownToGit(const QFileInfo
&path
)
432 QStringList arguments
;
433 arguments
<< "ls-files" << path
.filePath();
435 GitRunner
runner(git
, arguments
);
436 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
437 if (rc
!= AbstractCommand::Ok
)
439 bool unknownFile
= true;
442 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
443 if (lineLength
== -1)
445 unknownFile
= false; // lets assume here that the script just doesn't print anything if its not added yet.
448 git
.waitForFinished();