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