1#include "GitLocal.h"
2
3#include <GitBase.h>
4#include <GitWip.h>
5#include <QLogger.h>
6#include <RevisionFiles.h>
7
8#include <QFile>
9#include <QProcess>
10
11using namespace QLogger;
12
13namespace
14{
15static QString quote(const QStringList &sl)
16{
17 QString q(sl.join(QString("$%1$").arg(' ')));
18 q.prepend("$").append("$");
19 return q;
20}
21}
22
23GitLocal::GitLocal(const QSharedPointer<GitBase> &gitBase)
24 : mGitBase(gitBase)
25{
26}
27
28GitExecResult GitLocal::stageFile(const QString &fileName) const
29{
30 QLog_Debug("Git", QString("Staging file: {%1}").arg(fileName));
31
32 const auto cmd = QString("git add %1").arg(fileName);
33
34 QLog_Trace("Git", QString("Staging file: {%1}").arg(cmd));
35
36 const auto ret = mGitBase->run(cmd);
37
38 return ret;
39}
40
41bool GitLocal::isInCherryPickMerge() const
42{
43 QFile cherrypickHead(QString("%1/CHERRY_PICK_HEAD").arg(mGitBase->getGitDir()));
44
45 return cherrypickHead.exists();
46}
47
48GitExecResult GitLocal::cherryPickCommit(const QString &sha) const
49{
50 QLog_Debug("Git", QString("Cherry-picking commit: {%1}").arg(sha));
51
52 const auto cmd = QString(QString("git cherry-pick %1").arg(sha));
53
54 QLog_Trace("Git", QString("Cherry-picking commit: {%1}").arg(cmd));
55
56 const auto ret = mGitBase->run(cmd);
57
58 return ret;
59}
60
61GitExecResult GitLocal::cherryPickAbort() const
62{
63 QLog_Debug("Git", QString("Aborting cherryPick"));
64
65 const auto cmd = QString("git cherry-pick --abort");
66
67 QLog_Trace("Git", QString("Getting remote tags: {%1}").arg(cmd));
68
69 const auto ret = mGitBase->run(cmd);
70
71 return ret;
72}
73
74GitExecResult GitLocal::cherryPickContinue() const
75{
76 QLog_Debug("Git", QString("Applying cherryPick"));
77
78 const auto cmd = QString("git cherry-pick --continue");
79
80 QLog_Trace("Git", QString("Applying cherryPick: {%1}").arg(cmd));
81
82 const auto ret = mGitBase->run(cmd);
83
84 return ret;
85}
86
87GitExecResult GitLocal::checkoutCommit(const QString &sha) const
88{
89 QLog_Debug("Git", QString("Checking out a commit: {%1}").arg(sha));
90
91 const auto cmd = QString("git checkout %1").arg(sha);
92
93 QLog_Trace("Git", QString("Checking out a commit: {%1}").arg(cmd));
94
95 const auto ret = mGitBase->run(cmd);
96
97 if (ret.success)
98 mGitBase->updateCurrentBranch();
99
100 return ret;
101}
102
103GitExecResult GitLocal::markFilesAsResolved(const QStringList &files)
104{
105 QLog_Debug("Git", QString("Marking {%1} files as resolved").arg(files.count()));
106
107 const auto cmd = QString("git add %1").arg(files.join(" "));
108
109 QLog_Trace("Git", QString("Marking files as resolved: {%1}").arg(cmd));
110
111 const auto ret = mGitBase->run(cmd);
112
113 return ret;
114}
115
116bool GitLocal::checkoutFile(const QString &fileName) const
117{
118 if (fileName.isEmpty())
119 {
120 QLog_Warning("Git", QString("Executing checkoutFile with an empty file.").arg(fileName));
121
122 return false;
123 }
124
125 QLog_Debug("Git", QString("Checking out a file: {%1}").arg(fileName));
126
127 const auto cmd = QString("git checkout %1").arg(fileName);
128
129 QLog_Trace("Git", QString("Checking out a file: {%1}").arg(cmd));
130
131 const auto ret = mGitBase->run(cmd).success;
132
133 return ret;
134}
135
136GitExecResult GitLocal::resetFile(const QString &fileName) const
137{
138 QLog_Debug("Git", QString("Resetting file: {%1}").arg(fileName));
139
140 const auto cmd = QString("git reset %1").arg(fileName);
141
142 QLog_Trace("Git", QString("Getting remote tags: {%1}").arg(cmd));
143
144 const auto ret = mGitBase->run(cmd);
145
146 return ret;
147}
148
149bool GitLocal::resetCommit(const QString &sha, CommitResetType type)
150{
151 QString typeStr;
152
153 switch (type)
154 {
155 case CommitResetType::SOFT:
156 typeStr = "soft";
157 break;
158 case CommitResetType::MIXED:
159 typeStr = "mixed";
160 break;
161 case CommitResetType::HARD:
162 typeStr = "hard";
163 break;
164 }
165
166 QLog_Debug("Git", QString("Reseting commit: {%1} type {%2}").arg(sha, typeStr));
167
168 const auto cmd = QString("git reset --%1 %2").arg(typeStr, sha);
169
170 QLog_Trace("Git", QString("Reseting commit: {%1}").arg(cmd));
171
172 const auto ret = mGitBase->run(cmd);
173
174 return ret.success;
175}
176
177GitExecResult GitLocal::commit(const QString &msg) const
178{
179 QLog_Debug("Git", QString("Commit changes"));
180
181 const auto cmd = QString("git commit -m \"%1\"").arg(msg);
182 const auto ret = mGitBase->run(cmd);
183
184 return ret;
185}
186
187GitExecResult GitLocal::ammend(const QString &msg) const
188{
189 QLog_Debug("Git", QString("Amend message"));
190
191 const auto cmd = QString("git commit --amend -m \"%1\"").arg(msg);
192 return mGitBase->run(cmd);
193}
194
195GitExecResult GitLocal::commitFiles(QStringList &selFiles, const RevisionFiles &allCommitFiles,
196 const QString &msg) const
197{
198 QStringList notSel;
199
200 for (auto i = 0; i < allCommitFiles.count(); ++i)
201 {
202 if (const auto &fp = allCommitFiles.getFile(i);
203 selFiles.indexOf(fp) == -1 && allCommitFiles.statusCmp(i, RevisionFiles::IN_INDEX))
204 {
205 notSel.append(fp);
206 }
207 }
208
209 if (const auto updIdx = updateIndex(allCommitFiles, selFiles); !updIdx.success)
210 return updIdx;
211
212 QLog_Debug("Git", QString("Committing files"));
213
214 const auto cmd = QString("git commit -m \"%1\"").arg(msg);
215
216 QLog_Trace("Git", QString("Committing files: {%1}").arg(cmd));
217
218 auto ret = mGitBase->run(cmd);
219
220 if (ret.output.startsWith("On branch"))
221 ret.output = false;
222
223 return ret;
224}
225
226GitExecResult GitLocal::ammendCommit(const QStringList &selFiles, const RevisionFiles &allCommitFiles,
227 const QString &msg, const QString &author) const
228{
229 QStringList notSel;
230
231 for (auto i = 0; i < allCommitFiles.count(); ++i)
232 {
233 const QString &fp = allCommitFiles.getFile(i);
234 if (selFiles.indexOf(fp) == -1 && allCommitFiles.statusCmp(i, RevisionFiles::IN_INDEX)
235 && !allCommitFiles.statusCmp(i, RevisionFiles::DELETED))
236 notSel.append(fp);
237 }
238
239 QLog_Debug("Git", QString("Amending files"));
240
241 QString cmtOptions;
242
243 if (!author.isEmpty())
244 cmtOptions.append(QString(" --author \"%1\"").arg(author));
245
246 const auto cmd = QString("git commit --amend" + cmtOptions + " -m \"%1\"").arg(msg);
247
248 QLog_Trace("Git", QString("Amending files: {%1}").arg(cmd));
249
250 const auto ret = mGitBase->run(cmd);
251
252 return ret;
253}
254
255GitExecResult GitLocal::updateIndex(const RevisionFiles &files, const QStringList &selFiles) const
256{
257 QStringList toRemove;
258
259 for (const auto &file : selFiles)
260 {
261 const auto index = files.mFiles.indexOf(file);
262
263 if (index != -1 && files.statusCmp(index, RevisionFiles::DELETED))
264 toRemove << file;
265 }
266
267 if (!toRemove.isEmpty())
268 {
269 const auto cmd = QString("git rm --cached --ignore-unmatch -- " + quote(toRemove));
270
271 QLog_Trace("Git", QString("Updating index for files: {%1}").arg(cmd));
272
273 const auto ret = mGitBase->run(cmd);
274
275 if (!ret.success)
276 {
277
278 return ret;
279 }
280 }
281
282 const auto ret = GitExecResult(true, "Indexes updated");
283
284 return ret;
285}
286