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
) const
102 bool File::operator!=(const File
&other
) const
104 return ! (other
.d
== d
);
107 int File::count() const
109 return d
->hunks
.count();
112 void File::setOldFileName(const QByteArray
&filename
)
114 d
->oldFilename
= filename
;
115 d
->renameAcceptance
= d
->oldFilename
== d
->filename
? Vng::Accepted
: Vng::Undecided
;
118 QByteArray
File::oldFileName() const
120 return d
->oldFilename
;
123 void File::setProtection(const QString
&string
)
125 d
->protection
= string
;
126 d
->protectionAcceptance
= string
== d
->oldProtection
? Vng::Accepted
: Vng::Undecided
;
129 void File::setOldProtection(const QString
&string
)
131 d
->oldProtection
= string
;
132 d
->protectionAcceptance
= string
== d
->protection
? Vng::Accepted
: Vng::Undecided
;
135 QString
File::protection() const
137 return d
->protection
;
140 QString
File::oldProtection() const
142 return d
->oldProtection
;
146 void File::setOldSha1(const QString
&string
)
151 void File::setSha1(const QString
&string
)
156 QString
File::oldSha1() const
161 QString
File::sha1() const
166 bool File::hasChanged() const
168 return count() || d
->protection
!= d
->oldProtection
||
169 d
->filename
!= d
->oldFilename
;
172 void File::setRenameAcceptance(Vng::Acceptance accepted
)
174 if (d
->filename
!= d
->oldFilename
)
175 d
->renameAcceptance
= accepted
;
178 void File::setProtectionAcceptance(Vng::Acceptance accepted
)
180 if (d
->protection
!= d
->oldProtection
)
181 d
->protectionAcceptance
= accepted
;
184 Vng::Acceptance
File::renameAcceptance() const
186 return d
->renameAcceptance
;
189 Vng::Acceptance
File::protectionAcceptance() const
191 return d
->protectionAcceptance
;
194 QFile::Permissions
File::permissions() const
196 QFile::Permissions perms
;
197 if (d
->protection
.length() != 6)
199 Q_ASSERT(d
->protection
.length() == 6);
200 const int user
= d
->protection
.mid(3,1).toInt();
201 if (user
& 1) perms
|= QFile::ExeOwner
;
202 if (user
& 2) perms
|= QFile::WriteOwner
;
203 if (user
& 4) perms
|= QFile::ReadOwner
;
204 const int group
= d
->protection
.mid(4,1).toInt();
205 if (group
& 1) perms
|= QFile::ExeGroup
;
206 if (group
& 2) perms
|= QFile::WriteGroup
;
207 if (group
& 4) perms
|= QFile::ReadGroup
;
208 const int other
= d
->protection
.mid(5,1).toInt();
209 if (other
& 1) perms
|= QFile::ExeOther
;
210 if (other
& 2) perms
|= QFile::WriteOther
;
211 if (other
& 4) perms
|= QFile::ReadOther
;
215 int File::linesAdded() const
218 foreach(Hunk hunk
, d
->hunks
)
219 total
+= hunk
.linesAdded();
223 int File::linesRemoved() const
226 foreach(Hunk hunk
, d
->hunks
)
227 total
+= hunk
.linesRemoved();
231 void File::setBinary(bool binary
)
233 d
->isBinaryFile
= binary
;
236 bool File::isBinary() const
238 return d
->isBinaryFile
;
241 void File::fetchHunks(bool againstHead
)
243 if (d
->hunks
.count())
245 if (d
->oldSha1
== d
->sha1
)
246 return; // no change.
250 DiffCreator(File file
) : m_file(file
) { }
251 void wholeFileAsDiff(bool added
)
254 QStringList arguments
;
255 arguments
<< "cat-file" << "blob";
257 arguments
<< m_file
.sha1();
259 arguments
<< m_file
.oldSha1();
260 GitRunner
runner(git
, arguments
);
261 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
262 const char * prefix
= (added
? "+" : "-");
263 if (rc
== AbstractCommand::Ok
) {
265 Hunk hunk
; // its one hunk.
266 QByteArray
header("@@ -1,0 +1,0 @@", 15);
267 hunk
.addLine(header
);
269 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
270 if (lineLength
== -1)
272 QByteArray
array(prefix
, 1);
276 m_file
.addHunk(hunk
);
284 if (fileName().isEmpty()) { // deleted file.
285 DiffCreator
dc(*this);
286 dc
.wholeFileAsDiff(false);
290 QStringList arguments
;
291 if (sha1().startsWith("0000000") || oldSha1().startsWith("0000000")) {
292 // if this is changed without ever being added to the index, sha1() is 000.
293 // if added as a new file oldSha1() is 000
295 arguments
<< "diff-index" << "-p" << "HEAD" << fileName();
297 DiffCreator
dc(*this);
298 dc
.wholeFileAsDiff(true);
303 arguments
<< "diff" << oldSha1() << sha1();
305 GitRunner
runner(git
, arguments
);
306 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
309 ChangeSet::readGitDiff(git
, this);
312 void File::outputWhatsChanged(QTextStream
&out
, Configuration
&config
, bool printSummary
, bool unified
)
314 const bool deleted
= d
->filename
.isEmpty() && !d
->oldFilename
.isEmpty();
315 const bool added
= !deleted
&& !d
->filename
.isEmpty() && d
->oldFilename
.isEmpty();
316 const bool renamed
= !deleted
&& !added
&& d
->filename
!= d
->oldFilename
;
327 if (deleted
|| renamed
)
328 out
<< d
->oldFilename
;
332 out
<< " => " << d
->filename
;
334 int removed
= linesRemoved();
335 int added
= linesAdded();
337 out
<< " -" << QString::number(removed
);
339 out
<< " +" << QString::number(added
);
344 if (deleted
) { // file deleted.
346 config
.colorize(out
);
348 config
.normalColor(out
);
349 out
<< d
->oldFilename
<< endl
;
352 else if (added
) { // file added.
354 config
.colorize(out
);
356 config
.normalColor(out
);
357 out
<< d
->filename
<< endl
;
361 if(renamed
) { // rename
362 if (!unified
) config
.colorize(out
);
364 if (!unified
) config
.normalColor(out
);
365 out
<< "`" << d
->oldFilename
<< "' `" << d
->filename
<< "'" << endl
;
367 if (oldProtection() != protection()) {
368 if (!unified
) config
.colorize(out
);
369 out
<<"mode change ";
370 if (!unified
) config
.normalColor(out
);
371 out
<< d
->filename
<< " " << oldProtection() << " => " << protection() << endl
;
374 if (isBinary()) { // will not have any hunks
375 if (!unified
) config
.colorize(out
);
376 out
<< "binary modification ";
377 if (!unified
) config
.normalColor(out
);
378 out
<< d
->filename
<< endl
;
383 if (d
->oldFilename
.isEmpty())
384 out
<< "/dev/null\n";
386 out
<< d
->oldFilename
<< endl
;
388 if (d
->filename
.isEmpty())
389 out
<< "/dev/null\n";
391 out
<< d
->filename
<< endl
;
393 foreach(Hunk hunk
, hunks()) {
395 out
<< QString(hunk
.header());
396 out
<< QString(hunk
.patch());
399 for (int i
= 0; i
< hunk
.subHunkCount(); i
++) {
400 config
.colorize(out
);
402 QByteArray patch
= hunk
.subHunk(i
);
403 config
.normalColor(out
);
404 out
<< d
->filename
<<" "<< QString::number(hunk
.lineNumber(i
)) << endl
;
405 if (patch
.contains((char) 0)) { // binary
406 config
.colorize(out
);
407 out
<< "binary data\n";
408 config
.normalColor(out
);
411 QString
string (patch
);
412 const bool trailingLinefeed
= string
.length() > 1 && string
[string
.length()-1].unicode() == '\n';
413 if (config
.colorTerm() && trailingLinefeed
)
414 string
= string
.left(string
.length()-1);
416 if (config
.colorTerm() && (!trailingLinefeed
|| string
[string
.length()-1].isSpace())) {
417 config
.colorize2(out
);
419 config
.normalColor(out
);
428 void File::cleanHunksData()
434 bool File::fileKnownToGit(const QString
&path
)
436 return fileKnownToGit(QFileInfo(path
));
440 bool File::fileKnownToGit(const QFileInfo
&path
)
442 QStringList arguments
;
443 arguments
<< "ls-files" << path
.filePath();
445 GitRunner
runner(git
, arguments
);
446 AbstractCommand::ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
447 if (rc
!= AbstractCommand::Ok
)
449 bool unknownFile
= true;
452 qint64 lineLength
= Vng::readLine(&git
, buf
, sizeof(buf
));
453 if (lineLength
== -1)
455 unknownFile
= false; // lets assume here that the script just doesn't print anything if its not added yet.
458 git
.waitForFinished();