2 * This file is part of the vng project
3 * Copyright (C) 2008 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 "CommandLineParser.h"
21 #include "GitRunner.h"
24 #include "hunks/ChangeSet.h"
25 #include "commits/Commit.h"
29 static const CommandLineOption options
[] = {
30 // {"-a, --all", "answer yes to all patches"},
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 {"--matches PATTERN", "select patches matching PATTERN"},
39 {"-p, --patches REGEXP", "select patches matching REGEXP"},
40 // {"-t, --tags=REGEXP", "select tags matching REGEXP"},
41 // {"--context", "give output suitable for get --context"},
42 // {"--xml-output", "generate XML formatted output"},
43 // {"--human-readable", "give human-readable output"},
44 {"-s, --summary", "summarize changes"},
45 {"--no-summary", "don't summarize changes"},
46 // {"--reverse", "show changes in reverse order"},
51 : AbstractCommand("changes")
53 CommandLineParser::addOptionDefinitions(options
);
54 CommandLineParser::setArgumentDefinition("changes [FILE or DIRECTORY]" );
57 QString
Changes::argumentDescription() const
59 return "[FILE or DIRECTORY]";
62 QString
Changes::commandDescription() const
64 return "Gives a changelog-style summary of the repository history.\n";
67 AbstractCommand::ReturnCodes
Changes::run()
69 if (! checkInRepository())
71 CommandLineParser
*args
= CommandLineParser::instance();
82 : useRegExp(false), useRegExpTo(false), useMatcher(false), useMatcherTo(false)
84 CommandLineParser
*args
= CommandLineParser::instance();
86 if (args
->contains("to-match")) {
87 m_matcherTo
= QStringMatcher(args
->optionArgument("to-match"), Qt::CaseInsensitive
);
90 else if (args
->contains("to-patch")) {
91 m_regExpTo
= QRegExp(args
->optionArgument("to-patch"), Qt::CaseInsensitive
, QRegExp::RegExp2
);
96 if (args
->contains("from-match")) {
97 m_matcher
= QStringMatcher(args
->optionArgument("from-match"), Qt::CaseInsensitive
);
100 else if (args
->contains("from-patch")) {
101 m_regExp
= QRegExp(args
->optionArgument("from-patch"), Qt::CaseInsensitive
, QRegExp::RegExp2
);
105 m_state
= MatchingPerItem
;
106 findNormalMatchers();
110 ExpectedAction
match(const Commit
&commit
)
113 case AllClear
: return ShowPatch
;
115 if (useMatcher
&& m_matcher
.indexIn(commit
.author()) == -1
116 && m_matcher
.indexIn(commit
.logMessage()) == -1
117 || useRegExp
&& m_regExp
.indexIn(commit
.author()) == -1
118 && m_regExp
.indexIn(commit
.logMessage()) == -1)
120 m_state
= MatchingPerItem
;
121 findNormalMatchers();
122 return match(commit
);
123 case MatchingPerItem
:
124 if (useMatcher
&& m_matcher
.indexIn(commit
.author()) == -1
125 && m_matcher
.indexIn(commit
.logMessage()) == -1
126 || useRegExp
&& m_regExp
.indexIn(commit
.author()) == -1
127 && m_regExp
.indexIn(commit
.logMessage()) == -1)
129 if (!useMatcherTo
&& !useRegExpTo
)
132 if (useMatcherTo
&& m_matcherTo
.indexIn(commit
.author()) == -1
133 && m_matcherTo
.indexIn(commit
.logMessage()) == -1
134 || useRegExpTo
&& m_regExpTo
.indexIn(commit
.author()) == -1
135 && m_regExpTo
.indexIn(commit
.logMessage()) == -1)
146 void findNormalMatchers() {
147 CommandLineParser
*args
= CommandLineParser::instance();
148 if (args
->contains("matches")) {
149 m_matcher
= QStringMatcher(args
->optionArgument("matches"), Qt::CaseInsensitive
);
152 else if (args
->contains("patches")) {
153 m_regExp
= QRegExp(args
->optionArgument("patches"), Qt::CaseInsensitive
, QRegExp::RegExp2
);
156 else if (useMatcherTo
|| useRegExpTo
)
157 m_state
= SearchingForTo
;
164 AllClear
, // no matching to be done. All pass per definition.
165 BeforeFrom
, // searching for the 'from' All fail until we find that.
166 MatchingPerItem
,// Skipping / matching per item. As appropriate.
167 SearchingForTo
, // Pass all until we find the 'to' match.
168 AfterTo
// We are done, fail all.
170 bool useRegExp
, useRegExpTo
, useMatcher
, useMatcherTo
;
171 QRegExp m_regExp
, m_regExpTo
;
172 QStringMatcher m_matcher
, m_matcherTo
;
177 QStringList arguments
;
178 arguments
<< "whatchanged" << "-m" << "--pretty=raw";
180 if (args
->contains("last"))
181 arguments
<< "-n" << args
->optionArgument("last");
183 QList
<int> usedArguments
;
185 foreach (Branch branch
, m_config
.allBranches()) {
186 QString branchName
= branch
.branchName();
187 if (branchName
.endsWith("/HEAD"))
191 foreach (QString arg
, args
->arguments()) {
194 first
= false; // skip command, args for this command are next.
197 if (branchName
.endsWith(arg
) && branchName
[branchName
.length() - arg
.length() - 1] == '/') {
198 arguments
<< branchName
;
199 usedArguments
<< index
;
205 // now we have to use the rest of the arguments the user passed.
207 foreach (QString arg
, args
->arguments()) {
208 if (! usedArguments
.contains(index
++))
212 GitRunner
runner(git
, arguments
);
213 ReturnCodes rc
= runner
.start(GitRunner::WaitForStandardOutput
);
215 // TODO proper reporting
218 if (shouldUsePager())
219 Logger::startPager();
221 const bool showSummery
= m_config
.contains("summary") && !args
->contains("no-summary") || args
->contains("summary");
222 QTextStream
&out
= Logger::standardOut();
225 Commit commit
= Commit::createFromStream(&git
);
226 if (! commit
.isValid())
228 switch(matcher
.match(commit
)) {
229 case Matcher::SkipPatch
: continue;
230 case Matcher::ShowPatch
: break;
233 git
.waitForFinished();
237 out
<< commit
.commitTime().toString() << " ";
238 m_config
.colorize(out
);
239 out
<< commit
.author() << endl
;
240 m_config
.normalColor(out
);
241 out
<< " ID " << commit
.commitTreeIsm() << endl
;
242 out
<< commit
.logMessage();
243 if (Logger::verbosity() >= Logger::Verbose
|| showSummery
) {
245 ChangeSet cs
= commit
.changeSet();
247 foreach (File file
, cs
.files()) {
248 if (file
.oldFileName().isEmpty())
249 out
<<" A " << file
.fileName();
250 else if (file
.fileName().isEmpty())
251 out
<<" D " << file
.oldFileName();
253 out
<<" M " << file
.fileName();
254 if (Logger::verbosity() < Logger::Verbose
) {
255 if (file
.linesRemoved() > 0)
256 out
<< " -" << file
.linesRemoved();
257 if (file
.linesAdded() > 0)
258 out
<< " +" << file
.linesAdded();
261 if (Logger::verbosity() >= Logger::Verbose
)
262 file
.outputWhatsChanged(out
, m_config
, false, false);
266 Logger::flushPager();
269 git
.waitForFinished();