Make detection of empty repo also work after a git gc
[vng.git] / src / commands / Diff.cpp
blob445347f649cd688abb74135e91d01d48ea489558
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2009 Thomas Zander <tzander@trolltech.com>
4 * Copyright (C) 2002-2004 David Roundy
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "Diff.h"
21 #include "../CommandLineParser.h"
22 #include "../Logger.h"
23 #include "../GitRunner.h"
24 #include "../hunks/ChangeSet.h"
25 #include "WhatsNew.h"
26 #include "Changes.h"
28 #include <QDebug>
30 static const CommandLineOption options[] = {
31 {"--to-match PATTERN", "select changes up to a patch matching PATTERN"},
32 {"--to-patch REGEXP", "select changes up to a patch matching REGEXP"},
33 // {"--to-tag REGEXP", "select changes up to a tag matching REGEXP"},
34 {"--from-match PATTERN", "select changes starting with a patch matching PATTERN"},
35 {"--from-patch REGEXP", "select changes starting with a patch matching REGEXP"},
36 // {"--from-tag REGEXP", "select changes starting with a tag matching REGEXP"},
37 {"-n, --last NUMBER", "select the last NUMBER patches"},
38 {"--match PATTERN", "select patches matching PATTERN"},
39 {"-p, --patches REGEXP", "select patches matching REGEXP"},
40 // {"-t, --tags=REGEXP", "select tags matching REGEXP"},
41 {"--reverse", "show changes in reverse order"},
42 {"--no-reverse", "don't show changes in reverse order"},
43 CommandLineLastOption
46 Diff::Diff()
47 : AbstractCommand("diff")
49 CommandLineParser::addOptionDefinitions(options);
50 CommandLineParser::setArgumentDefinition("diff [FILE or DIRECTORY]" );
53 AbstractCommand::ReturnCodes Diff::run()
55 if (! checkInRepository())
56 return NotInRepo;
57 CommandLineParser *args = CommandLineParser::instance();
59 QStringList arguments;
60 arguments << QLatin1String("diff-tree") << QLatin1String("-p");
62 bool parsedArguments = false;
63 if (args->arguments().count() > 1) {
64 foreach (Branch branch, m_config.allBranches()) { // check if our argument is a named branch.
65 QString branchName = branch.branchName();
66 if (branchName.endsWith(QLatin1String("/HEAD")))
67 continue;
68 bool first = true;
69 int index = -1;
70 foreach (QString arg, args->arguments()) {
71 index++;
72 if (first) {
73 first = false; // skip command, args for this command are next.
74 continue;
76 if (branchName == arg || (branchName.endsWith(arg) && branchName[branchName.length() - arg.length() - 1].unicode() == '/')) {
77 // its a named branch!
78 arguments << branch.commitTreeIsmSha1() << QLatin1String("HEAD");
79 parsedArguments = true;
80 break;
83 if (parsedArguments)
84 break;
87 if (!parsedArguments) { // check if our arguments are files on the filesystem.
88 QStringList files;
89 QStringList revisions;
91 QString currentDir = QDir::current().absolutePath();
92 moveToRoot(CheckFileSystem);
93 foreach (QString arg, rebasedArguments()) {
94 QFile file (arg);
95 if (file.exists()) {
96 files << arg;
97 } else {
98 revisions << arg;
101 QDir::setCurrent(currentDir);
102 if (revisions.count() > 2) {
103 Logger::error() << "Vng Failed: unknown files or revisions passed";
104 return InvalidOptions;
106 //qDebug() << " revisions" << revisions << "files" << files;
107 // TODO if files have been passed we can't handle it with the diff-tree, so this will fail
109 // split our arguments in files and revisions
110 if (!revisions.isEmpty()) {
111 arguments << revisions;
112 if (revisions.count() == 1)
113 arguments << QLatin1String("HEAD");
114 if (! files.isEmpty())
115 arguments << QLatin1String("--");
117 arguments << files;
118 parsedArguments = true;
122 const QStringList options = args->options();
123 // always use 'whatsnew' unless the user passes a patch options
124 if (!parsedArguments && !(options.contains(QLatin1String("to-match"))
125 || options.contains(QLatin1String("to-patch"))
126 || options.contains(QLatin1String("from-patch"))
127 || options.contains(QLatin1String("from-match"))
128 || options.contains(QLatin1String("last"))
129 || options.contains(QLatin1String("match")))) {
130 WhatsNew wn;
131 wn.pullConfigDataFrom(this);
132 return wn.printWhatsNew(true, false);
135 if (shouldUsePager())
136 Logger::startPager();
138 if (!parsedArguments) {
139 QString revisions;
140 Changes changes;
141 changes.pullConfigDataFrom(this);
142 ReturnCodes rc = changes.printChangeList(true, revisions);
143 if (rc != Ok)
144 return rc;
145 QStringList revs = revisions.split(QLatin1Char(' '));
146 if (revs.count() == 2) {
147 arguments << revs.at(1);
148 arguments << revs.at(0);
152 QProcess git;
153 GitRunner runner(git, arguments);
154 ReturnCodes rc = runner.start(GitRunner::WaitForStandardOutput, GitRunner::FailureAccepted);
155 if (rc == AbstractCommand::GitFailed) { // wrong args, most likely
156 Logger::error() << "Vng failed: failed to understand arguments\n";
157 return InvalidOptions;
159 if (rc) {
160 Logger::error() << "Vng failed: Unknown reason\n";
161 return rc;
164 QTextStream &out = Logger::standardOut();
165 foreach (File file, ChangeSet::readGitDiff(git)) {
166 file.outputWhatsChanged(out, m_config, false, true);
168 Logger::stopPager();
170 return Ok;
173 QString Diff::argumentDescription() const
175 return QLatin1String("[FILE or DIRECTORY]");
178 QString Diff::commandDescription() const
180 return QLatin1String("Diff can be used to create a diff between two versions which are in your\n"
181 "repository. Specifying just --from-patch will get you a diff against\n"
182 "your working copy. If you give diff no version arguments, it gives\n"
183 "you the same information as whatsnew except that the patch is\n"
184 "formatted as the output of a diff command\n");