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