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