1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.api;
44
45 import java.io.IOException;
46 import java.text.MessageFormat;
47 import java.util.Collection;
48 import java.util.LinkedList;
49
50 import org.eclipse.jgit.api.errors.CheckoutConflictException;
51 import org.eclipse.jgit.api.errors.GitAPIException;
52 import org.eclipse.jgit.api.errors.JGitInternalException;
53 import org.eclipse.jgit.dircache.DirCache;
54 import org.eclipse.jgit.dircache.DirCacheBuildIterator;
55 import org.eclipse.jgit.dircache.DirCacheBuilder;
56 import org.eclipse.jgit.dircache.DirCacheCheckout;
57 import org.eclipse.jgit.dircache.DirCacheEntry;
58 import org.eclipse.jgit.dircache.DirCacheIterator;
59 import org.eclipse.jgit.internal.JGitText;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.NullProgressMonitor;
62 import org.eclipse.jgit.lib.ObjectId;
63 import org.eclipse.jgit.lib.ProgressMonitor;
64 import org.eclipse.jgit.lib.Ref;
65 import org.eclipse.jgit.lib.RefUpdate;
66 import org.eclipse.jgit.lib.Repository;
67 import org.eclipse.jgit.lib.RepositoryState;
68 import org.eclipse.jgit.revwalk.RevCommit;
69 import org.eclipse.jgit.revwalk.RevWalk;
70 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
71 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
72 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
73 import org.eclipse.jgit.treewalk.TreeWalk;
74 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
75
76
77
78
79
80
81
82
83
84
85 public class ResetCommand extends GitCommand<Ref> {
86
87
88
89
90 public enum ResetType {
91
92
93
94 SOFT,
95
96
97
98
99 MIXED,
100
101
102
103
104 HARD,
105
106
107
108
109
110
111 MERGE,
112
113
114
115
116
117 KEEP
118 }
119
120
121
122 private String ref = null;
123
124 private ResetType mode;
125
126 private Collection<String> filepaths = new LinkedList<>();
127
128 private boolean isReflogDisabled;
129
130 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
131
132
133
134
135
136
137
138
139
140 public ResetCommand(Repository repo) {
141 super(repo);
142 }
143
144
145
146
147
148
149
150
151 @Override
152 public Ref call() throws GitAPIException, CheckoutConflictException {
153 checkCallable();
154
155 try {
156 RepositoryState state = repo.getRepositoryState();
157 final boolean merging = state.equals(RepositoryState.MERGING)
158 || state.equals(RepositoryState.MERGING_RESOLVED);
159 final boolean cherryPicking = state
160 .equals(RepositoryState.CHERRY_PICKING)
161 || state.equals(RepositoryState.CHERRY_PICKING_RESOLVED);
162 final boolean reverting = state.equals(RepositoryState.REVERTING)
163 || state.equals(RepositoryState.REVERTING_RESOLVED);
164
165 final ObjectId commitId = resolveRefToCommitId();
166
167 if (ref != null && commitId == null) {
168
169
170 throw new JGitInternalException(MessageFormat
171 .format(JGitText.get().invalidRefName, ref));
172 }
173
174 final ObjectId commitTree;
175 if (commitId != null)
176 commitTree = parseCommit(commitId).getTree();
177 else
178 commitTree = null;
179
180 if (!filepaths.isEmpty()) {
181
182 resetIndexForPaths(commitTree);
183 setCallable(false);
184 return repo.exactRef(Constants.HEAD);
185 }
186
187 final Ref result;
188 if (commitId != null) {
189
190 final RefUpdate ru = repo.updateRef(Constants.HEAD);
191 ru.setNewObjectId(commitId);
192
193 String refName = Repository.shortenRefName(getRefOrHEAD());
194 if (isReflogDisabled) {
195 ru.disableRefLog();
196 } else {
197 String message = refName + ": updating " + Constants.HEAD;
198 ru.setRefLogMessage(message, false);
199 }
200 if (ru.forceUpdate() == RefUpdate.Result.LOCK_FAILURE)
201 throw new JGitInternalException(MessageFormat.format(
202 JGitText.get().cannotLock, ru.getName()));
203
204 ObjectId origHead = ru.getOldObjectId();
205 if (origHead != null)
206 repo.writeOrigHead(origHead);
207 }
208 result = repo.exactRef(Constants.HEAD);
209
210 if (mode == null)
211 mode = ResetType.MIXED;
212
213 switch (mode) {
214 case HARD:
215 checkoutIndex(commitTree);
216 break;
217 case MIXED:
218 resetIndex(commitTree);
219 break;
220 case SOFT:
221 break;
222 case KEEP:
223 case MERGE:
224 throw new UnsupportedOperationException();
225
226 }
227
228 if (mode != ResetType.SOFT) {
229 if (merging)
230 resetMerge();
231 else if (cherryPicking)
232 resetCherryPick();
233 else if (reverting)
234 resetRevert();
235 else if (repo.readSquashCommitMsg() != null)
236 repo.writeSquashCommitMsg(null );
237 }
238
239 setCallable(false);
240 return result;
241 } catch (IOException e) {
242 throw new JGitInternalException(MessageFormat.format(
243 JGitText.get().exceptionCaughtDuringExecutionOfResetCommand,
244 e.getMessage()), e);
245 }
246 }
247
248 private RevCommit parseCommit(ObjectId commitId) {
249 try (RevWalkRevWalk.html#RevWalk">RevWalk rw = new RevWalk(repo)) {
250 return rw.parseCommit(commitId);
251 } catch (IOException e) {
252 throw new JGitInternalException(MessageFormat.format(
253 JGitText.get().cannotReadCommit, commitId.toString()), e);
254 }
255 }
256
257 private ObjectId resolveRefToCommitId() {
258 try {
259 return repo.resolve(getRefOrHEAD() + "^{commit}");
260 } catch (IOException e) {
261 throw new JGitInternalException(
262 MessageFormat.format(JGitText.get().cannotRead, getRefOrHEAD()),
263 e);
264 }
265 }
266
267
268
269
270
271
272
273
274 public ResetCommand setRef(String ref) {
275 this.ref = ref;
276 return this;
277 }
278
279
280
281
282
283
284
285
286 public ResetCommand setMode(ResetType mode) {
287 if (!filepaths.isEmpty())
288 throw new JGitInternalException(MessageFormat.format(
289 JGitText.get().illegalCombinationOfArguments,
290 "[--mixed | --soft | --hard]", "<paths>..."));
291 this.mode = mode;
292 return this;
293 }
294
295
296
297
298
299
300
301
302
303 public ResetCommand addPath(String path) {
304 if (mode != null)
305 throw new JGitInternalException(MessageFormat.format(
306 JGitText.get().illegalCombinationOfArguments, "<paths>...",
307 "[--mixed | --soft | --hard]"));
308 filepaths.add(path);
309 return this;
310 }
311
312
313
314
315
316
317
318
319
320
321 public ResetCommand disableRefLog(boolean disable) {
322 this.isReflogDisabled = disable;
323 return this;
324 }
325
326
327
328
329
330
331
332 public boolean isReflogDisabled() {
333 return this.isReflogDisabled;
334 }
335
336 private String getRefOrHEAD() {
337 if (ref != null)
338 return ref;
339 else
340 return Constants.HEAD;
341 }
342
343
344
345
346
347
348
349
350
351
352
353 public ResetCommand setProgressMonitor(ProgressMonitor monitor) {
354 if (monitor == null) {
355 monitor = NullProgressMonitor.INSTANCE;
356 }
357 this.monitor = monitor;
358 return this;
359 }
360
361 private void resetIndexForPaths(ObjectId commitTree) {
362 DirCache dc = null;
363 try (TreeWalkTreeWalk.html#TreeWalk">TreeWalk tw = new TreeWalk(repo)) {
364 dc = repo.lockDirCache();
365 DirCacheBuilder builder = dc.builder();
366
367 tw.addTree(new DirCacheBuildIterator(builder));
368 if (commitTree != null)
369 tw.addTree(commitTree);
370 else
371 tw.addTree(new EmptyTreeIterator());
372 tw.setFilter(PathFilterGroup.createFromStrings(filepaths));
373 tw.setRecursive(true);
374
375 while (tw.next()) {
376 final CanonicalTreeParser tree = tw.getTree(1,
377 CanonicalTreeParser.class);
378
379 if (tree != null) {
380
381 DirCacheEntry entry = new DirCacheEntry(tw.getRawPath());
382 entry.setFileMode(tree.getEntryFileMode());
383 entry.setObjectId(tree.getEntryObjectId());
384 builder.add(entry);
385 }
386 }
387
388 builder.commit();
389 } catch (IOException e) {
390 throw new RuntimeException(e);
391 } finally {
392 if (dc != null)
393 dc.unlock();
394 }
395 }
396
397 private void resetIndex(ObjectId commitTree) throws IOException {
398 DirCache dc = repo.lockDirCache();
399 try (TreeWalkeeWalk.html#TreeWalk">TreeWalk walk = new TreeWalk(repo)) {
400 DirCacheBuilder builder = dc.builder();
401
402 if (commitTree != null)
403 walk.addTree(commitTree);
404 else
405 walk.addTree(new EmptyTreeIterator());
406 walk.addTree(new DirCacheIterator(dc));
407 walk.setRecursive(true);
408
409 while (walk.next()) {
410 AbstractTreeIterator cIter = walk.getTree(0,
411 AbstractTreeIterator.class);
412 if (cIter == null) {
413
414 continue;
415 }
416
417 final DirCacheEntryEntry.html#DirCacheEntry">DirCacheEntry entry = new DirCacheEntry(walk.getRawPath());
418 entry.setFileMode(cIter.getEntryFileMode());
419 entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset());
420
421 DirCacheIterator dcIter = walk.getTree(1,
422 DirCacheIterator.class);
423 if (dcIter != null && dcIter.idEqual(cIter)) {
424 DirCacheEntry indexEntry = dcIter.getDirCacheEntry();
425 entry.setLastModified(indexEntry.getLastModified());
426 entry.setLength(indexEntry.getLength());
427 }
428
429 builder.add(entry);
430 }
431
432 builder.commit();
433 } finally {
434 dc.unlock();
435 }
436 }
437
438 private void checkoutIndex(ObjectId commitTree) throws IOException,
439 GitAPIException {
440 DirCache dc = repo.lockDirCache();
441 try {
442 DirCacheCheckout checkout = new DirCacheCheckout(repo, dc,
443 commitTree);
444 checkout.setFailOnConflict(false);
445 checkout.setProgressMonitor(monitor);
446 try {
447 checkout.checkout();
448 } catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
449 throw new CheckoutConflictException(checkout.getConflicts(),
450 cce);
451 }
452 } finally {
453 dc.unlock();
454 }
455 }
456
457 private void resetMerge() throws IOException {
458 repo.writeMergeHeads(null);
459 repo.writeMergeCommitMsg(null);
460 }
461
462 private void resetCherryPick() throws IOException {
463 repo.writeCherryPickHead(null);
464 repo.writeMergeCommitMsg(null);
465 }
466
467 private void resetRevert() throws IOException {
468 repo.writeRevertHead(null);
469 repo.writeMergeCommitMsg(null);
470 }
471
472
473 @SuppressWarnings("nls")
474 @Override
475 public String toString() {
476 return "ResetCommand [repo=" + repo + ", ref=" + ref + ", mode=" + mode
477 + ", isReflogDisabled=" + isReflogDisabled + ", filepaths="
478 + filepaths + "]";
479 }
480
481 }