Adding some code to post-process the unrecord data
[vng.git] / commands / UnRecord.cpp
bloba56abfa50d3fa420e8d3adba6f99306458ad6d45
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
4 * Copyright (C) 2003-2005 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 "UnRecord.h"
21 #include "CommandLineParser.h"
22 #include "Interview.h"
23 #include "GitRunner.h"
24 #include "Logger.h"
25 #include "hunks/ChangeSet.h"
26 #include "patches/Commit.h"
27 #include "patches/CommitsCursor.h"
29 #include <QDebug>
31 static const CommandLineOption options[] = {
32 {"--from-match PATTERN", "select changes starting with a patch matching PATTERN"},
33 {"--from-patch REGEXP", "select changes starting with a patch matching REGEXP"},
34 //{"--from-tag REGEXP", "select changes starting with a tag matching REGEXP"},
35 {"-n, --last NUMBER", "select the last NUMBER patches"},
36 {"-a, --all", "select all patches till the first branchpoint found"},
37 {"--match PATTERN", "select patches matching PATTERN"},
38 {"-p, --patches REGEXP", "select patches matching REGEXP"},
39 //{"-t REGEXP,--tags REGEXP", "select tags matching REGEXP"},
40 CommandLineLastOption
43 UnRecord::UnRecord()
44 :AbstractCommand("unrecord")
46 CommandLineParser::addOptionDefinitions(options);
49 QString UnRecord::argumentDescription() const
51 return QString();
54 QString UnRecord::commandDescription() const
56 return "UnRecord does the opposite of record in that it makes the changes from\n"
57 "patches active changes again which you may record or revert later. The\n"
58 "working copy itself will not change.\n";
61 AbstractCommand::ReturnCodes UnRecord::run()
63 if (! checkInRepository())
64 return NotInRepo;
65 if (m_config.isEmptyRepo()) {
66 Logger::error() << "Vng failed: Can not run unrecord on a repo without recorded pathes\n";
67 return Disabled;
70 CommandLineParser *args = CommandLineParser::instance();
72 Commit acceptedCommit;
73 int unrecordCount = 0;
74 bool oneAutoAcceptedPatch = false;
75 if (args->contains("all")) {
76 Commit head("HEAD");
77 Commit commit = head.firstCommitInBranch();
78 if (! commit.isValid()) {
79 Logger::error() << "Vng failed: Could not find the branch point, are you sure you are on a branch?\n";
80 return OtherVngError;
82 acceptedCommit = commit;
83 while(true) {
84 unrecordCount++;
85 if (commit == head)
86 break;
87 commit = commit.next();
88 oneAutoAcceptedPatch = true;
91 else { // use a cursor
92 CommitsCursor cursor;
93 cursor.setUseMatcher(true);
94 if (args->contains("last")) {
95 int count = args->optionArgument("last").toInt();
96 for (int i = 0; i < count && cursor.isValid(); i++) {
97 cursor.setResponse(true);
98 cursor.forward();
101 else {
102 Interview interview(cursor, name());
103 interview.setUsePager(shouldUsePager());
104 if (! interview.start()) {
105 Logger::warn() << "unrecord cancelled." << endl;
106 return Ok;
110 Commit commit = cursor.head();
111 while (true) {
112 if (commit.acceptance() == Vng::Rejected) // can't use this one.
113 break;
114 else if (commit.acceptance() == Vng::Accepted)
115 acceptedCommit = commit;
116 if (commit.previousCommitsCount() == 0) // at first commit.
117 break;
118 if (commit.acceptance() == Vng::Undecided) {
119 if (acceptedCommit.isValid()) // already found a 'yes'.
120 break;
121 oneAutoAcceptedPatch = true;
123 commit = commit.previous()[0];
124 if (unrecordCount >= cursor.oldestCommitAltered())
125 break;
126 unrecordCount++;
128 acceptedCommit = commit;
131 if (unrecordCount == 0 || !acceptedCommit.isValid()) {
132 Logger::warn() << "Ok, if you don't want to unrecord anything, that's fine!\n";
133 return Ok;
135 if (oneAutoAcceptedPatch) {
136 QString answer = Interview::ask(QString("Do you really want to unrecord %1 patches? ").arg(unrecordCount));
137 if (! (answer.startsWith("y") || answer.startsWith("Y")))
138 return Ok;
140 QProcess git;
141 QStringList arguments;
142 arguments << "diff-index" << acceptedCommit.commitTreeIsm(); // get all the affected files.
143 GitRunner runner(git, arguments);
144 ReturnCodes rc = runner.start(GitRunner::WaitForStandardOutput);
145 if (rc) {
146 Logger::error() << "Vng failed: Internal error" << endl;
147 return rc;
149 Commit commit = Commit::createFromStream(&git);
151 QStringList addFilesList; // list of files that we have to git-add since they were not known in this revision.
152 QStringList resetFileList; // list of files that we need to git-reset to make the index notice their diffs
153 ChangeSet changeSet = commit.changeSet();
154 for (int i=0; i < changeSet.count(); ++i) {
155 File file = changeSet.file(i);
156 if (file.oldFileName().isEmpty()) {
157 qDebug() << "file was added" << file.fileName();
158 addFilesList << file.fileName();
160 else if (file.fileName().isEmpty()) {
161 qDebug() << "file was removed" << file.oldFileName();
162 addFilesList << file.oldFileName();
164 else { // reset
165 qDebug() << "file was modified" << file.fileName();
166 addFilesList << file.fileName();
170 if (dryRun())
171 return Ok;
173 arguments.clear();
174 arguments << "update-ref" << "HEAD" << acceptedCommit.commitTreeIsm();
175 runner.setArguments(arguments);
176 rc = runner.start(GitRunner::WaitUntilFinished);
177 if (rc != Ok) {
178 Logger::error() << "Failed to update the ref, sorry.\n";
179 return rc;
183 return Ok;