Notice when the pager is killed and kill the git process so we can have a lot better...
[vng.git] / Changes.cpp
blob93c40a5d1c4f6cd73251fd224f6b94885504163b
1 /*
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/>.
19 #include "Changes.h"
20 #include "CommandLineParser.h"
21 #include "GitRunner.h"
22 #include "Logger.h"
23 #include "Vng.h"
24 #include "hunks/ChangeSet.h"
25 #include "commits/Commit.h"
26 #include "commits/CommitsMatcher.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 {"--match 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"},
47 CommandLineLastOption
50 Changes::Changes()
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())
70 return NotInRepo;
71 CommandLineParser *args = CommandLineParser::instance();
73 QProcess git;
74 QStringList arguments;
75 arguments << "whatchanged" << "-m" << "--pretty=raw";
77 if (args->contains("last"))
78 arguments << "-n" << args->optionArgument("last");
80 QList<int> usedArguments;
81 usedArguments << 0;
82 foreach (Branch branch, m_config.allBranches()) {
83 QString branchName = branch.branchName();
84 if (branchName.endsWith("/HEAD"))
85 continue;
86 bool first = true;
87 int index = -1;
88 foreach (QString arg, args->arguments()) {
89 index++;
90 if (first) {
91 first = false; // skip command, args for this command are next.
92 continue;
94 if (branchName == arg || branchName.endsWith(arg) && branchName[branchName.length() - arg.length() - 1] == '/') {
95 arguments << branchName;
96 usedArguments << index;
97 break;
102 // now we have to use the rest of the arguments the user passed.
103 int index = 0;
104 foreach (QString arg, args->arguments()) {
105 if (! usedArguments.contains(index++))
106 arguments << arg;
109 GitRunner runner(git, arguments);
110 ReturnCodes rc = runner.start(GitRunner::WaitForStandardOutput);
111 if (rc) {
112 // TODO proper reporting
113 return rc;
115 if (shouldUsePager())
116 Logger::startPager();
118 const bool showSummery = m_config.contains("summary") && !args->contains("no-summary") || args->contains("summary");
119 QTextStream &out = Logger::standardOut();
120 CommitsMatcher matcher;
121 while(true) {
122 Commit commit = Commit::createFromStream(&git);
123 if (! commit.isValid())
124 break;
125 switch(matcher.match(commit)) {
126 case CommitsMatcher::SkipPatch: continue;
127 case CommitsMatcher::ShowPatch: break;
128 case CommitsMatcher::Exit:
129 Logger::stopPager();
130 git.waitForFinished();
131 return Ok;
134 out << commit.commitTime().toString() << " ";
135 m_config.colorize(out);
136 out << commit.author() << endl;
137 m_config.normalColor(out);
138 out << " ID " << commit.commitTreeIsm() << endl;
139 out << commit.logMessage();
140 if (Logger::verbosity() >= Logger::Verbose || showSummery) {
141 out << endl;
142 ChangeSet cs = commit.changeSet();
143 cs.generateHunks();
144 foreach (File file, cs.files()) {
145 if (file.oldFileName().isEmpty())
146 out <<" A " << file.fileName();
147 else if (file.fileName().isEmpty())
148 out <<" D " << file.oldFileName();
149 else
150 out <<" M " << file.fileName();
151 if (Logger::verbosity() < Logger::Verbose) {
152 if (file.linesRemoved() > 0)
153 out << " -" << file.linesRemoved();
154 if (file.linesAdded() > 0)
155 out << " +" << file.linesAdded();
157 out << endl;
158 if (Logger::verbosity() >= Logger::Verbose)
159 file.outputWhatsChanged(out, m_config, false, false);
162 out << endl;
163 Logger::flushPager();
164 if (! out.device()->isWritable()) { // output cancelled; lets kill git.
165 git.kill();
166 break;
169 Logger::stopPager();
170 git.waitForFinished();
171 return Ok;