View Javadoc
1   /*
2    * Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler@sap.com>
3    * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue@obeo.fr> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  package org.eclipse.jgit.api;
12  
13  import static java.nio.charset.StandardCharsets.UTF_8;
14  
15  import java.io.ByteArrayOutputStream;
16  import java.io.File;
17  import java.io.FileNotFoundException;
18  import java.io.FileOutputStream;
19  import java.io.IOException;
20  import java.text.MessageFormat;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  import org.eclipse.jgit.api.RebaseResult.Status;
33  import org.eclipse.jgit.api.ResetCommand.ResetType;
34  import org.eclipse.jgit.api.errors.CheckoutConflictException;
35  import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
36  import org.eclipse.jgit.api.errors.GitAPIException;
37  import org.eclipse.jgit.api.errors.InvalidRebaseStepException;
38  import org.eclipse.jgit.api.errors.InvalidRefNameException;
39  import org.eclipse.jgit.api.errors.JGitInternalException;
40  import org.eclipse.jgit.api.errors.NoHeadException;
41  import org.eclipse.jgit.api.errors.NoMessageException;
42  import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
43  import org.eclipse.jgit.api.errors.RefNotFoundException;
44  import org.eclipse.jgit.api.errors.StashApplyFailureException;
45  import org.eclipse.jgit.api.errors.UnmergedPathsException;
46  import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
47  import org.eclipse.jgit.diff.DiffFormatter;
48  import org.eclipse.jgit.dircache.DirCache;
49  import org.eclipse.jgit.dircache.DirCacheCheckout;
50  import org.eclipse.jgit.dircache.DirCacheIterator;
51  import org.eclipse.jgit.errors.RevisionSyntaxException;
52  import org.eclipse.jgit.internal.JGitText;
53  import org.eclipse.jgit.lib.AbbreviatedObjectId;
54  import org.eclipse.jgit.lib.AnyObjectId;
55  import org.eclipse.jgit.lib.ConfigConstants;
56  import org.eclipse.jgit.lib.Constants;
57  import org.eclipse.jgit.lib.NullProgressMonitor;
58  import org.eclipse.jgit.lib.ObjectId;
59  import org.eclipse.jgit.lib.ObjectReader;
60  import org.eclipse.jgit.lib.PersonIdent;
61  import org.eclipse.jgit.lib.ProgressMonitor;
62  import org.eclipse.jgit.lib.RebaseTodoLine;
63  import org.eclipse.jgit.lib.RebaseTodoLine.Action;
64  import org.eclipse.jgit.lib.Ref;
65  import org.eclipse.jgit.lib.RefUpdate;
66  import org.eclipse.jgit.lib.RefUpdate.Result;
67  import org.eclipse.jgit.lib.Repository;
68  import org.eclipse.jgit.merge.MergeStrategy;
69  import org.eclipse.jgit.revwalk.RevCommit;
70  import org.eclipse.jgit.revwalk.RevSort;
71  import org.eclipse.jgit.revwalk.RevWalk;
72  import org.eclipse.jgit.revwalk.filter.RevFilter;
73  import org.eclipse.jgit.submodule.SubmoduleWalk.IgnoreSubmoduleMode;
74  import org.eclipse.jgit.treewalk.TreeWalk;
75  import org.eclipse.jgit.treewalk.filter.TreeFilter;
76  import org.eclipse.jgit.util.FileUtils;
77  import org.eclipse.jgit.util.IO;
78  import org.eclipse.jgit.util.RawParseUtils;
79  
80  /**
81   * A class used to execute a {@code Rebase} command. It has setters for all
82   * supported options and arguments of this command and a {@link #call()} method
83   * to finally execute the command. Each instance of this class should only be
84   * used for one invocation of the command (means: one call to {@link #call()})
85   * <p>
86   *
87   * @see <a
88   *      href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"
89   *      >Git documentation about Rebase</a>
90   */
91  public class RebaseCommand extends GitCommand<RebaseResult> {
92  	/**
93  	 * The name of the "rebase-merge" folder for interactive rebases.
94  	 */
95  	public static final String REBASE_MERGE = "rebase-merge"; //$NON-NLS-1$
96  
97  	/**
98  	 * The name of the "rebase-apply" folder for non-interactive rebases.
99  	 */
100 	private static final String REBASE_APPLY = "rebase-apply"; //$NON-NLS-1$
101 
102 	/**
103 	 * The name of the "stopped-sha" file
104 	 */
105 	public static final String STOPPED_SHA = "stopped-sha"; //$NON-NLS-1$
106 
107 	private static final String AUTHOR_SCRIPT = "author-script"; //$NON-NLS-1$
108 
109 	private static final String DONE = "done"; //$NON-NLS-1$
110 
111 	private static final String GIT_AUTHOR_DATE = "GIT_AUTHOR_DATE"; //$NON-NLS-1$
112 
113 	private static final String GIT_AUTHOR_EMAIL = "GIT_AUTHOR_EMAIL"; //$NON-NLS-1$
114 
115 	private static final String GIT_AUTHOR_NAME = "GIT_AUTHOR_NAME"; //$NON-NLS-1$
116 
117 	private static final String GIT_REBASE_TODO = "git-rebase-todo"; //$NON-NLS-1$
118 
119 	private static final String HEAD_NAME = "head-name"; //$NON-NLS-1$
120 
121 	private static final String INTERACTIVE = "interactive"; //$NON-NLS-1$
122 
123 	private static final String QUIET = "quiet"; //$NON-NLS-1$
124 
125 	private static final String MESSAGE = "message"; //$NON-NLS-1$
126 
127 	private static final String ONTO = "onto"; //$NON-NLS-1$
128 
129 	private static final String ONTO_NAME = "onto_name"; //$NON-NLS-1$
130 
131 	private static final String PATCH = "patch"; //$NON-NLS-1$
132 
133 	private static final String REBASE_HEAD = "orig-head"; //$NON-NLS-1$
134 
135 	/** Pre git 1.7.6 file name for {@link #REBASE_HEAD}. */
136 	private static final String REBASE_HEAD_LEGACY = "head"; //$NON-NLS-1$
137 
138 	private static final String AMEND = "amend"; //$NON-NLS-1$
139 
140 	private static final String MESSAGE_FIXUP = "message-fixup"; //$NON-NLS-1$
141 
142 	private static final String MESSAGE_SQUASH = "message-squash"; //$NON-NLS-1$
143 
144 	private static final String AUTOSTASH = "autostash"; //$NON-NLS-1$
145 
146 	private static final String AUTOSTASH_MSG = "On {0}: autostash"; //$NON-NLS-1$
147 
148 	/**
149 	 * The folder containing the hashes of (potentially) rewritten commits when
150 	 * --preserve-merges is used.
151 	 * <p>
152 	 * Native git rebase --merge uses a <em>file</em> of that name to record
153 	 * commits to copy notes at the end of the whole rebase.
154 	 * </p>
155 	 */
156 	private static final String REWRITTEN = "rewritten"; //$NON-NLS-1$
157 
158 	/**
159 	 * File containing the current commit(s) to cherry pick when --preserve-merges
160 	 * is used.
161 	 */
162 	private static final String CURRENT_COMMIT = "current-commit"; //$NON-NLS-1$
163 
164 	private static final String REFLOG_PREFIX = "rebase:"; //$NON-NLS-1$
165 
166 	/**
167 	 * The available operations
168 	 */
169 	public enum Operation {
170 		/**
171 		 * Initiates rebase
172 		 */
173 		BEGIN,
174 		/**
175 		 * Continues after a conflict resolution
176 		 */
177 		CONTINUE,
178 		/**
179 		 * Skips the "current" commit
180 		 */
181 		SKIP,
182 		/**
183 		 * Aborts and resets the current rebase
184 		 */
185 		ABORT,
186 		/**
187 		 * Starts processing steps
188 		 * @since 3.2
189 		 */
190 		PROCESS_STEPS;
191 	}
192 
193 	private Operation operation = Operation.BEGIN;
194 
195 	private RevCommit upstreamCommit;
196 
197 	private String upstreamCommitName;
198 
199 	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
200 
201 	private final RevWalk walk;
202 
203 	private final RebaseState rebaseState;
204 
205 	private InteractiveHandler interactiveHandler;
206 
207 	private boolean stopAfterInitialization = false;
208 
209 	private RevCommit newHead;
210 
211 	private boolean lastStepWasForward;
212 
213 	private MergeStrategy strategy = MergeStrategy.RECURSIVE;
214 
215 	private boolean preserveMerges = false;
216 
217 	/**
218 	 * <p>
219 	 * Constructor for RebaseCommand.
220 	 * </p>
221 	 *
222 	 * @param repo
223 	 *            the {@link org.eclipse.jgit.lib.Repository}
224 	 */
225 	protected RebaseCommand(Repository repo) {
226 		super(repo);
227 		walk = new RevWalk(repo);
228 		rebaseState = new RebaseState(repo.getDirectory());
229 	}
230 
231 	/**
232 	 * {@inheritDoc}
233 	 * <p>
234 	 * Executes the {@code Rebase} command with all the options and parameters
235 	 * collected by the setter methods of this class. Each instance of this
236 	 * class should only be used for one invocation of the command. Don't call
237 	 * this method twice on an instance.
238 	 */
239 	@Override
240 	public RebaseResult call() throws GitAPIException, NoHeadException,
241 			RefNotFoundException, WrongRepositoryStateException {
242 		newHead = null;
243 		lastStepWasForward = false;
244 		checkCallable();
245 		checkParameters();
246 		try {
247 			switch (operation) {
248 			case ABORT:
249 				try {
250 					return abort(RebaseResult.ABORTED_RESULT);
251 				} catch (IOException ioe) {
252 					throw new JGitInternalException(ioe.getMessage(), ioe);
253 				}
254 			case PROCESS_STEPS:
255 			case SKIP:
256 			case CONTINUE:
257 				String upstreamCommitId = rebaseState.readFile(ONTO);
258 				try {
259 					upstreamCommitName = rebaseState.readFile(ONTO_NAME);
260 				} catch (FileNotFoundException e) {
261 					// Fall back to commit ID if file doesn't exist (e.g. rebase
262 					// was started by C Git)
263 					upstreamCommitName = upstreamCommitId;
264 				}
265 				this.upstreamCommit = walk.parseCommit(repo
266 						.resolve(upstreamCommitId));
267 				preserveMerges = rebaseState.getRewrittenDir().isDirectory();
268 				break;
269 			case BEGIN:
270 				autoStash();
271 				if (stopAfterInitialization
272 						|| !walk.isMergedInto(
273 								walk.parseCommit(repo.resolve(Constants.HEAD)),
274 								upstreamCommit)) {
275 					org.eclipse.jgit.api.Status status = Git.wrap(repo)
276 							.status().setIgnoreSubmodules(IgnoreSubmoduleMode.ALL).call();
277 					if (status.hasUncommittedChanges()) {
278 						List<String> list = new ArrayList<>();
279 						list.addAll(status.getUncommittedChanges());
280 						return RebaseResult.uncommittedChanges(list);
281 					}
282 				}
283 				RebaseResult res = initFilesAndRewind();
284 				if (stopAfterInitialization)
285 					return RebaseResult.INTERACTIVE_PREPARED_RESULT;
286 				if (res != null) {
287 					autoStashApply();
288 					if (rebaseState.getDir().exists())
289 						FileUtils.delete(rebaseState.getDir(),
290 								FileUtils.RECURSIVE);
291 					return res;
292 				}
293 			}
294 
295 			if (monitor.isCancelled())
296 				return abort(RebaseResult.ABORTED_RESULT);
297 
298 			if (operation == Operation.CONTINUE) {
299 				newHead = continueRebase();
300 				List<RebaseTodoLine> doneLines = repo.readRebaseTodo(
301 						rebaseState.getPath(DONE), true);
302 				RebaseTodoLine step = doneLines.get(doneLines.size() - 1);
303 				if (newHead != null
304 						&& step.getAction() != Action.PICK) {
305 					RebaseTodoLine newStep = new RebaseTodoLine(
306 							step.getAction(),
307 							AbbreviatedObjectId.fromObjectId(newHead),
308 							step.getShortMessage());
309 					RebaseResult result = processStep(newStep, false);
310 					if (result != null)
311 						return result;
312 				}
313 				File amendFile = rebaseState.getFile(AMEND);
314 				boolean amendExists = amendFile.exists();
315 				if (amendExists) {
316 					FileUtils.delete(amendFile);
317 				}
318 				if (newHead == null && !amendExists) {
319 					// continueRebase() returns null only if no commit was
320 					// neccessary. This means that no changes where left over
321 					// after resolving all conflicts. In this case, cgit stops
322 					// and displays a nice message to the user, telling him to
323 					// either do changes or skip the commit instead of continue.
324 					return RebaseResult.NOTHING_TO_COMMIT_RESULT;
325 				}
326 			}
327 
328 			if (operation == Operation.SKIP)
329 				newHead = checkoutCurrentHead();
330 
331 			List<RebaseTodoLine> steps = repo.readRebaseTodo(
332 					rebaseState.getPath(GIT_REBASE_TODO), false);
333 			if (steps.isEmpty()) {
334 				return finishRebase(walk.parseCommit(repo.resolve(Constants.HEAD)), false);
335 			}
336 			if (isInteractive()) {
337 				interactiveHandler.prepareSteps(steps);
338 				repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO),
339 						steps, false);
340 			}
341 			checkSteps(steps);
342 			for (RebaseTodoLine step : steps) {
343 				popSteps(1);
344 				RebaseResult result = processStep(step, true);
345 				if (result != null) {
346 					return result;
347 				}
348 			}
349 			return finishRebase(newHead, lastStepWasForward);
350 		} catch (CheckoutConflictException cce) {
351 			return RebaseResult.conflicts(cce.getConflictingPaths());
352 		} catch (IOException ioe) {
353 			throw new JGitInternalException(ioe.getMessage(), ioe);
354 		}
355 	}
356 
357 	private void autoStash() throws GitAPIException, IOException {
358 		if (repo.getConfig().getBoolean(ConfigConstants.CONFIG_REBASE_SECTION,
359 				ConfigConstants.CONFIG_KEY_AUTOSTASH, false)) {
360 			String message = MessageFormat.format(
361 							AUTOSTASH_MSG,
362 							Repository
363 									.shortenRefName(getHeadName(getHead())));
364 			RevCommit stashCommit = Git.wrap(repo).stashCreate().setRef(null)
365 					.setWorkingDirectoryMessage(
366 							message)
367 					.call();
368 			if (stashCommit != null) {
369 				FileUtils.mkdir(rebaseState.getDir());
370 				rebaseState.createFile(AUTOSTASH, stashCommit.getName());
371 			}
372 		}
373 	}
374 
375 	private boolean autoStashApply() throws IOException, GitAPIException {
376 		boolean conflicts = false;
377 		if (rebaseState.getFile(AUTOSTASH).exists()) {
378 			String stash = rebaseState.readFile(AUTOSTASH);
379 			try (Git git = Git.wrap(repo)) {
380 				git.stashApply().setStashRef(stash)
381 						.ignoreRepositoryState(true).setStrategy(strategy)
382 						.call();
383 			} catch (StashApplyFailureException e) {
384 				conflicts = true;
385 				try (RevWalkRevWalk.html#RevWalk">RevWalk rw = new RevWalk(repo)) {
386 					ObjectId stashId = repo.resolve(stash);
387 					RevCommit commit = rw.parseCommit(stashId);
388 					updateStashRef(commit, commit.getAuthorIdent(),
389 							commit.getShortMessage());
390 				}
391 			}
392 		}
393 		return conflicts;
394 	}
395 
396 	private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent,
397 			String refLogMessage) throws IOException {
398 		Ref currentRef = repo.exactRef(Constants.R_STASH);
399 		RefUpdate refUpdate = repo.updateRef(Constants.R_STASH);
400 		refUpdate.setNewObjectId(commitId);
401 		refUpdate.setRefLogIdent(refLogIdent);
402 		refUpdate.setRefLogMessage(refLogMessage, false);
403 		refUpdate.setForceRefLog(true);
404 		if (currentRef != null)
405 			refUpdate.setExpectedOldObjectId(currentRef.getObjectId());
406 		else
407 			refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
408 		refUpdate.forceUpdate();
409 	}
410 
411 	private RebaseResult processStep(RebaseTodoLine step, boolean shouldPick)
412 			throws IOException, GitAPIException {
413 		if (Action.COMMENT.equals(step.getAction()))
414 			return null;
415 		if (preserveMerges
416 				&& shouldPick
417 				&& (Action.EDIT.equals(step.getAction()) || Action.PICK
418 						.equals(step.getAction()))) {
419 			writeRewrittenHashes();
420 		}
421 		ObjectReader or = repo.newObjectReader();
422 
423 		Collection<ObjectId> ids = or.resolve(step.getCommit());
424 		if (ids.size() != 1)
425 			throw new JGitInternalException(
426 					JGitText.get().cannotResolveUniquelyAbbrevObjectId);
427 		RevCommit commitToPick = walk.parseCommit(ids.iterator().next());
428 		if (shouldPick) {
429 			if (monitor.isCancelled())
430 				return RebaseResult.result(Status.STOPPED, commitToPick);
431 			RebaseResult result = cherryPickCommit(commitToPick);
432 			if (result != null)
433 				return result;
434 		}
435 		boolean isSquash = false;
436 		switch (step.getAction()) {
437 		case PICK:
438 			return null; // continue rebase process on pick command
439 		case REWORD:
440 			String oldMessage = commitToPick.getFullMessage();
441 			String newMessage = interactiveHandler
442 					.modifyCommitMessage(oldMessage);
443 			try (Gitit.html#Git">Git git = new Git(repo)) {
444 				newHead = git.commit().setMessage(newMessage).setAmend(true)
445 						.setNoVerify(true).call();
446 			}
447 			return null;
448 		case EDIT:
449 			rebaseState.createFile(AMEND, commitToPick.name());
450 			return stop(commitToPick, Status.EDIT);
451 		case COMMENT:
452 			break;
453 		case SQUASH:
454 			isSquash = true;
455 			//$FALL-THROUGH$
456 		case FIXUP:
457 			resetSoftToParent();
458 			List<RebaseTodoLine> steps = repo.readRebaseTodo(
459 					rebaseState.getPath(GIT_REBASE_TODO), false);
460 			RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0);
461 			File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP);
462 			File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH);
463 			if (isSquash && messageFixupFile.exists())
464 				messageFixupFile.delete();
465 			newHead = doSquashFixup(isSquash, commitToPick, nextStep,
466 					messageFixupFile, messageSquashFile);
467 		}
468 		return null;
469 	}
470 
471 	private RebaseResult cherryPickCommit(RevCommit commitToPick)
472 			throws IOException, GitAPIException, NoMessageException,
473 			UnmergedPathsException, ConcurrentRefUpdateException,
474 			WrongRepositoryStateException, NoHeadException {
475 		try {
476 			monitor.beginTask(MessageFormat.format(
477 					JGitText.get().applyingCommit,
478 					commitToPick.getShortMessage()), ProgressMonitor.UNKNOWN);
479 			if (preserveMerges) {
480 				return cherryPickCommitPreservingMerges(commitToPick);
481 			}
482 			return cherryPickCommitFlattening(commitToPick);
483 		} finally {
484 			monitor.endTask();
485 		}
486 	}
487 
488 	private RebaseResult cherryPickCommitFlattening(RevCommit commitToPick)
489 			throws IOException, GitAPIException, NoMessageException,
490 			UnmergedPathsException, ConcurrentRefUpdateException,
491 			WrongRepositoryStateException, NoHeadException {
492 		// If the first parent of commitToPick is the current HEAD,
493 		// we do a fast-forward instead of cherry-pick to avoid
494 		// unnecessary object rewriting
495 		newHead = tryFastForward(commitToPick);
496 		lastStepWasForward = newHead != null;
497 		if (!lastStepWasForward) {
498 			// TODO if the content of this commit is already merged
499 			// here we should skip this step in order to avoid
500 			// confusing pseudo-changed
501 			String ourCommitName = getOurCommitName();
502 			try (Gitit.html#Git">Git git = new Git(repo)) {
503 				CherryPickResult cherryPickResult = git.cherryPick()
504 					.include(commitToPick).setOurCommitName(ourCommitName)
505 					.setReflogPrefix(REFLOG_PREFIX).setStrategy(strategy)
506 					.call();
507 				switch (cherryPickResult.getStatus()) {
508 				case FAILED:
509 					if (operation == Operation.BEGIN) {
510 						return abort(RebaseResult
511 								.failed(cherryPickResult.getFailingPaths()));
512 					}
513 					return stop(commitToPick, Status.STOPPED);
514 				case CONFLICTING:
515 					return stop(commitToPick, Status.STOPPED);
516 				case OK:
517 					newHead = cherryPickResult.getNewHead();
518 				}
519 			}
520 		}
521 		return null;
522 	}
523 
524 	private RebaseResult cherryPickCommitPreservingMerges(RevCommit commitToPick)
525 			throws IOException, GitAPIException, NoMessageException,
526 			UnmergedPathsException, ConcurrentRefUpdateException,
527 			WrongRepositoryStateException, NoHeadException {
528 
529 		writeCurrentCommit(commitToPick);
530 
531 		List<RevCommit> newParents = getNewParents(commitToPick);
532 		boolean otherParentsUnchanged = true;
533 		for (int i = 1; i < commitToPick.getParentCount(); i++)
534 			otherParentsUnchanged &= newParents.get(i).equals(
535 					commitToPick.getParent(i));
536 		// If the first parent of commitToPick is the current HEAD,
537 		// we do a fast-forward instead of cherry-pick to avoid
538 		// unnecessary object rewriting
539 		newHead = otherParentsUnchanged ? tryFastForward(commitToPick) : null;
540 		lastStepWasForward = newHead != null;
541 		if (!lastStepWasForward) {
542 			ObjectId headId = getHead().getObjectId();
543 			// getHead() checks for null
544 			assert headId != null;
545 			if (!AnyObjectId.isEqual(headId, newParents.get(0)))
546 				checkoutCommit(headId.getName(), newParents.get(0));
547 
548 			// Use the cherry-pick strategy if all non-first parents did not
549 			// change. This is different from C Git, which always uses the merge
550 			// strategy (see below).
551 			try (Gitit.html#Git">Git git = new Git(repo)) {
552 				if (otherParentsUnchanged) {
553 					boolean isMerge = commitToPick.getParentCount() > 1;
554 					String ourCommitName = getOurCommitName();
555 					CherryPickCommand pickCommand = git.cherryPick()
556 							.include(commitToPick)
557 							.setOurCommitName(ourCommitName)
558 							.setReflogPrefix(REFLOG_PREFIX)
559 							.setStrategy(strategy);
560 					if (isMerge) {
561 						pickCommand.setMainlineParentNumber(1);
562 						// We write a MERGE_HEAD and later commit explicitly
563 						pickCommand.setNoCommit(true);
564 						writeMergeInfo(commitToPick, newParents);
565 					}
566 					CherryPickResult cherryPickResult = pickCommand.call();
567 					switch (cherryPickResult.getStatus()) {
568 					case FAILED:
569 						if (operation == Operation.BEGIN) {
570 							return abort(RebaseResult.failed(
571 									cherryPickResult.getFailingPaths()));
572 						}
573 						return stop(commitToPick, Status.STOPPED);
574 					case CONFLICTING:
575 						return stop(commitToPick, Status.STOPPED);
576 					case OK:
577 						if (isMerge) {
578 							// Commit the merge (setup above using
579 							// writeMergeInfo())
580 							CommitCommand commit = git.commit();
581 							commit.setAuthor(commitToPick.getAuthorIdent());
582 							commit.setReflogComment(REFLOG_PREFIX + " " //$NON-NLS-1$
583 									+ commitToPick.getShortMessage());
584 							newHead = commit.call();
585 						} else
586 							newHead = cherryPickResult.getNewHead();
587 						break;
588 					}
589 				} else {
590 					// Use the merge strategy to redo merges, which had some of
591 					// their non-first parents rewritten
592 					MergeCommand merge = git.merge()
593 							.setFastForward(MergeCommand.FastForwardMode.NO_FF)
594 							.setProgressMonitor(monitor)
595 							.setCommit(false);
596 					for (int i = 1; i < commitToPick.getParentCount(); i++)
597 						merge.include(newParents.get(i));
598 					MergeResult mergeResult = merge.call();
599 					if (mergeResult.getMergeStatus().isSuccessful()) {
600 						CommitCommand commit = git.commit();
601 						commit.setAuthor(commitToPick.getAuthorIdent());
602 						commit.setMessage(commitToPick.getFullMessage());
603 						commit.setReflogComment(REFLOG_PREFIX + " " //$NON-NLS-1$
604 								+ commitToPick.getShortMessage());
605 						newHead = commit.call();
606 					} else {
607 						if (operation == Operation.BEGIN && mergeResult
608 								.getMergeStatus() == MergeResult.MergeStatus.FAILED)
609 							return abort(RebaseResult
610 									.failed(mergeResult.getFailingPaths()));
611 						return stop(commitToPick, Status.STOPPED);
612 					}
613 				}
614 			}
615 		}
616 		return null;
617 	}
618 
619 	// Prepare MERGE_HEAD and message for the next commit
620 	private void writeMergeInfo(RevCommit commitToPick,
621 			List<RevCommit> newParents) throws IOException {
622 		repo.writeMergeHeads(newParents.subList(1, newParents.size()));
623 		repo.writeMergeCommitMsg(commitToPick.getFullMessage());
624 	}
625 
626 	// Get the rewritten equivalents for the parents of the given commit
627 	private List<RevCommit> getNewParents(RevCommit commitToPick)
628 			throws IOException {
629 		List<RevCommit> newParents = new ArrayList<>();
630 		for (int p = 0; p < commitToPick.getParentCount(); p++) {
631 			String parentHash = commitToPick.getParent(p).getName();
632 			if (!new File(rebaseState.getRewrittenDir(), parentHash).exists())
633 				newParents.add(commitToPick.getParent(p));
634 			else {
635 				String newParent = RebaseState.readFile(
636 						rebaseState.getRewrittenDir(), parentHash);
637 				if (newParent.length() == 0)
638 					newParents.add(walk.parseCommit(repo
639 							.resolve(Constants.HEAD)));
640 				else
641 					newParents.add(walk.parseCommit(ObjectId
642 							.fromString(newParent)));
643 			}
644 		}
645 		return newParents;
646 	}
647 
648 	private void writeCurrentCommit(RevCommit commit) throws IOException {
649 		RebaseState.appendToFile(rebaseState.getFile(CURRENT_COMMIT),
650 				commit.name());
651 	}
652 
653 	private void writeRewrittenHashes() throws RevisionSyntaxException,
654 			IOException, RefNotFoundException {
655 		File currentCommitFile = rebaseState.getFile(CURRENT_COMMIT);
656 		if (!currentCommitFile.exists())
657 			return;
658 
659 		ObjectId headId = getHead().getObjectId();
660 		// getHead() checks for null
661 		assert headId != null;
662 		String head = headId.getName();
663 		String currentCommits = rebaseState.readFile(CURRENT_COMMIT);
664 		for (String current : currentCommits.split("\n")) //$NON-NLS-1$
665 			RebaseState
666 					.createFile(rebaseState.getRewrittenDir(), current, head);
667 		FileUtils.delete(currentCommitFile);
668 	}
669 
670 	private RebaseResult finishRebase(RevCommit finalHead,
671 			boolean lastStepIsForward) throws IOException, GitAPIException {
672 		String headName = rebaseState.readFile(HEAD_NAME);
673 		updateHead(headName, finalHead, upstreamCommit);
674 		boolean stashConflicts = autoStashApply();
675 		getRepository().autoGC(monitor);
676 		FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
677 		if (stashConflicts)
678 			return RebaseResult.STASH_APPLY_CONFLICTS_RESULT;
679 		if (lastStepIsForward || finalHead == null)
680 			return RebaseResult.FAST_FORWARD_RESULT;
681 		return RebaseResult.OK_RESULT;
682 	}
683 
684 	private void checkSteps(List<RebaseTodoLine> steps)
685 			throws InvalidRebaseStepException, IOException {
686 		if (steps.isEmpty())
687 			return;
688 		if (RebaseTodoLine.Action.SQUASH.equals(steps.get(0).getAction())
689 				|| RebaseTodoLine.Action.FIXUP.equals(steps.get(0).getAction())) {
690 			if (!rebaseState.getFile(DONE).exists()
691 					|| rebaseState.readFile(DONE).trim().length() == 0) {
692 				throw new InvalidRebaseStepException(MessageFormat.format(
693 						JGitText.get().cannotSquashFixupWithoutPreviousCommit,
694 						steps.get(0).getAction().name()));
695 			}
696 		}
697 
698 	}
699 
700 	private RevCommit>RevCommit doSquashFixup(boolean isSquash, RevCommit commitToPick,
701 			RebaseTodoLine nextStep, File messageFixup, File messageSquash)
702 			throws IOException, GitAPIException {
703 
704 		if (!messageSquash.exists()) {
705 			// init squash/fixup sequence
706 			ObjectId headId = repo.resolve(Constants.HEAD);
707 			RevCommit previousCommit = walk.parseCommit(headId);
708 
709 			initializeSquashFixupFile(MESSAGE_SQUASH,
710 					previousCommit.getFullMessage());
711 			if (!isSquash)
712 				initializeSquashFixupFile(MESSAGE_FIXUP,
713 					previousCommit.getFullMessage());
714 		}
715 		String currSquashMessage = rebaseState
716 				.readFile(MESSAGE_SQUASH);
717 
718 		int count = parseSquashFixupSequenceCount(currSquashMessage) + 1;
719 
720 		String content = composeSquashMessage(isSquash,
721 				commitToPick, currSquashMessage, count);
722 		rebaseState.createFile(MESSAGE_SQUASH, content);
723 		if (messageFixup.exists())
724 			rebaseState.createFile(MESSAGE_FIXUP, content);
725 
726 		return squashIntoPrevious(
727 				!messageFixup.exists(),
728 				nextStep);
729 	}
730 
731 	private void resetSoftToParent() throws IOException,
732 			GitAPIException, CheckoutConflictException {
733 		Ref ref = repo.exactRef(Constants.ORIG_HEAD);
734 		ObjectId orig_head = ref == null ? null : ref.getObjectId();
735 		try (Git git = Git.wrap(repo)) {
736 			// we have already committed the cherry-picked commit.
737 			// what we need is to have changes introduced by this
738 			// commit to be on the index
739 			// resetting is a workaround
740 			git.reset().setMode(ResetType.SOFT)
741 					.setRef("HEAD~1").call(); //$NON-NLS-1$
742 		} finally {
743 			// set ORIG_HEAD back to where we started because soft
744 			// reset moved it
745 			repo.writeOrigHead(orig_head);
746 		}
747 	}
748 
749 	private RevCommit squashIntoPrevious(boolean sequenceContainsSquash,
750 			RebaseTodoLine nextStep)
751 			throws IOException, GitAPIException {
752 		RevCommit retNewHead;
753 		String commitMessage = rebaseState
754 				.readFile(MESSAGE_SQUASH);
755 
756 		try (Gitit.html#Git">Git git = new Git(repo)) {
757 			if (nextStep == null || ((nextStep.getAction() != Action.FIXUP)
758 					&& (nextStep.getAction() != Action.SQUASH))) {
759 				// this is the last step in this sequence
760 				if (sequenceContainsSquash) {
761 					commitMessage = interactiveHandler
762 							.modifyCommitMessage(commitMessage);
763 				}
764 				retNewHead = git.commit()
765 						.setMessage(stripCommentLines(commitMessage))
766 						.setAmend(true).setNoVerify(true).call();
767 				rebaseState.getFile(MESSAGE_SQUASH).delete();
768 				rebaseState.getFile(MESSAGE_FIXUP).delete();
769 
770 			} else {
771 				// Next step is either Squash or Fixup
772 				retNewHead = git.commit().setMessage(commitMessage)
773 						.setAmend(true).setNoVerify(true).call();
774 			}
775 		}
776 		return retNewHead;
777 	}
778 
779 	private static String stripCommentLines(String commitMessage) {
780 		StringBuilder result = new StringBuilder();
781 		for (String line : commitMessage.split("\n")) { //$NON-NLS-1$
782 			if (!line.trim().startsWith("#")) //$NON-NLS-1$
783 				result.append(line).append("\n"); //$NON-NLS-1$
784 		}
785 		if (!commitMessage.endsWith("\n")) { //$NON-NLS-1$
786 			int bufferSize = result.length();
787 			if (bufferSize > 0 && result.charAt(bufferSize - 1) == '\n') {
788 				result.deleteCharAt(bufferSize - 1);
789 			}
790 		}
791 		return result.toString();
792 	}
793 
794 	@SuppressWarnings("nls")
795 	private static String composeSquashMessage(boolean isSquash,
796 			RevCommit commitToPick, String currSquashMessage, int count) {
797 		StringBuilder sb = new StringBuilder();
798 		String ordinal = getOrdinal(count);
799 		sb.setLength(0);
800 		sb.append("# This is a combination of ").append(count)
801 				.append(" commits.\n");
802 		// Add the previous message without header (i.e first line)
803 		sb.append(currSquashMessage
804 				.substring(currSquashMessage.indexOf('\n') + 1));
805 		sb.append("\n");
806 		if (isSquash) {
807 			sb.append("# This is the ").append(count).append(ordinal)
808 					.append(" commit message:\n");
809 			sb.append(commitToPick.getFullMessage());
810 		} else {
811 			sb.append("# The ").append(count).append(ordinal)
812 					.append(" commit message will be skipped:\n# ");
813 			sb.append(commitToPick.getFullMessage().replaceAll("([\n\r])",
814 					"$1# "));
815 		}
816 		return sb.toString();
817 	}
818 
819 	private static String getOrdinal(int count) {
820 		switch (count % 10) {
821 		case 1:
822 			return "st"; //$NON-NLS-1$
823 		case 2:
824 			return "nd"; //$NON-NLS-1$
825 		case 3:
826 			return "rd"; //$NON-NLS-1$
827 		default:
828 			return "th"; //$NON-NLS-1$
829 		}
830 	}
831 
832 	/**
833 	 * Parse the count from squashed commit messages
834 	 *
835 	 * @param currSquashMessage
836 	 *            the squashed commit message to be parsed
837 	 * @return the count of squashed messages in the given string
838 	 */
839 	static int parseSquashFixupSequenceCount(String currSquashMessage) {
840 		String regex = "This is a combination of (.*) commits"; //$NON-NLS-1$
841 		String firstLine = currSquashMessage.substring(0,
842 				currSquashMessage.indexOf('\n'));
843 		Pattern pattern = Pattern.compile(regex);
844 		Matcher matcher = pattern.matcher(firstLine);
845 		if (!matcher.find())
846 			throw new IllegalArgumentException();
847 		return Integer.parseInt(matcher.group(1));
848 	}
849 
850 	private void initializeSquashFixupFile(String messageFile,
851 			String fullMessage) throws IOException {
852 		rebaseState
853 				.createFile(
854 						messageFile,
855 						"# This is a combination of 1 commits.\n# The first commit's message is:\n" + fullMessage); //$NON-NLS-1$);
856 	}
857 
858 	private String getOurCommitName() {
859 		// If onto is different from upstream, this should say "onto", but
860 		// RebaseCommand doesn't support a different "onto" at the moment.
861 		String ourCommitName = "Upstream, based on " //$NON-NLS-1$
862 				+ Repository.shortenRefName(upstreamCommitName);
863 		return ourCommitName;
864 	}
865 
866 	private void updateHead(String headName, RevCommit../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit aNewHead, RevCommit onto)
867 			throws IOException {
868 		// point the previous head (if any) to the new commit
869 
870 		if (headName.startsWith(Constants.R_REFS)) {
871 			RefUpdate rup = repo.updateRef(headName);
872 			rup.setNewObjectId(aNewHead);
873 			rup.setRefLogMessage("rebase finished: " + headName + " onto " //$NON-NLS-1$ //$NON-NLS-2$
874 					+ onto.getName(), false);
875 			Result res = rup.forceUpdate();
876 			switch (res) {
877 			case FAST_FORWARD:
878 			case FORCED:
879 			case NO_CHANGE:
880 				break;
881 			default:
882 				throw new JGitInternalException(
883 						JGitText.get().updatingHeadFailed);
884 			}
885 			rup = repo.updateRef(Constants.HEAD);
886 			rup.setRefLogMessage("rebase finished: returning to " + headName, //$NON-NLS-1$
887 					false);
888 			res = rup.link(headName);
889 			switch (res) {
890 			case FAST_FORWARD:
891 			case FORCED:
892 			case NO_CHANGE:
893 				break;
894 			default:
895 				throw new JGitInternalException(
896 						JGitText.get().updatingHeadFailed);
897 			}
898 		}
899 	}
900 
901 	private RevCommit checkoutCurrentHead() throws IOException, NoHeadException {
902 		ObjectId headTree = repo.resolve(Constants.HEAD + "^{tree}"); //$NON-NLS-1$
903 		if (headTree == null)
904 			throw new NoHeadException(
905 					JGitText.get().cannotRebaseWithoutCurrentHead);
906 		DirCache dc = repo.lockDirCache();
907 		try {
908 			DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree);
909 			dco.setFailOnConflict(false);
910 			dco.setProgressMonitor(monitor);
911 			boolean needsDeleteFiles = dco.checkout();
912 			if (needsDeleteFiles) {
913 				List<String> fileList = dco.getToBeDeleted();
914 				for (String filePath : fileList) {
915 					File fileToDelete = new File(repo.getWorkTree(), filePath);
916 					if (repo.getFS().exists(fileToDelete))
917 						FileUtils.delete(fileToDelete, FileUtils.RECURSIVE
918 								| FileUtils.RETRY);
919 				}
920 			}
921 		} finally {
922 			dc.unlock();
923 		}
924 		try (RevWalkRevWalk.html#RevWalk">RevWalk rw = new RevWalk(repo)) {
925 			RevCommit commit = rw.parseCommit(repo.resolve(Constants.HEAD));
926 			return commit;
927 		}
928 	}
929 
930 	/**
931 	 * @return the commit if we had to do a commit, otherwise null
932 	 * @throws GitAPIException
933 	 * @throws IOException
934 	 */
935 	private RevCommit continueRebase() throws GitAPIException, IOException {
936 		// if there are still conflicts, we throw a specific Exception
937 		DirCache dc = repo.readDirCache();
938 		boolean hasUnmergedPaths = dc.hasUnmergedPaths();
939 		if (hasUnmergedPaths)
940 			throw new UnmergedPathsException();
941 
942 		// determine whether we need to commit
943 		boolean needsCommit;
944 		try (TreeWalklk.html#TreeWalk">TreeWalk treeWalk = new TreeWalk(repo)) {
945 			treeWalk.reset();
946 			treeWalk.setRecursive(true);
947 			treeWalk.addTree(new DirCacheIterator(dc));
948 			ObjectId id = repo.resolve(Constants.HEAD + "^{tree}"); //$NON-NLS-1$
949 			if (id == null)
950 				throw new NoHeadException(
951 						JGitText.get().cannotRebaseWithoutCurrentHead);
952 
953 			treeWalk.addTree(id);
954 
955 			treeWalk.setFilter(TreeFilter.ANY_DIFF);
956 
957 			needsCommit = treeWalk.next();
958 		}
959 		if (needsCommit) {
960 			try (Gitit.html#Git">Git git = new Git(repo)) {
961 				CommitCommand commit = git.commit();
962 				commit.setMessage(rebaseState.readFile(MESSAGE));
963 				commit.setAuthor(parseAuthor());
964 				return commit.call();
965 			}
966 		}
967 		return null;
968 	}
969 
970 	private PersonIdent parseAuthor() throws IOException {
971 		File authorScriptFile = rebaseState.getFile(AUTHOR_SCRIPT);
972 		byte[] raw;
973 		try {
974 			raw = IO.readFully(authorScriptFile);
975 		} catch (FileNotFoundException notFound) {
976 			if (authorScriptFile.exists()) {
977 				throw notFound;
978 			}
979 			return null;
980 		}
981 		return parseAuthor(raw);
982 	}
983 
984 	private RebaseResult stop(RevCommit commitToPick, RebaseResult.Status status)
985 			throws IOException {
986 		PersonIdent author = commitToPick.getAuthorIdent();
987 		String authorScript = toAuthorScript(author);
988 		rebaseState.createFile(AUTHOR_SCRIPT, authorScript);
989 		rebaseState.createFile(MESSAGE, commitToPick.getFullMessage());
990 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
991 		try (DiffFormattertter.html#DiffFormatter">DiffFormatter df = new DiffFormatter(bos)) {
992 			df.setRepository(repo);
993 			df.format(commitToPick.getParent(0), commitToPick);
994 		}
995 		rebaseState.createFile(PATCH, new String(bos.toByteArray(), UTF_8));
996 		rebaseState.createFile(STOPPED_SHA,
997 				repo.newObjectReader()
998 				.abbreviate(
999 				commitToPick).name());
1000 		// Remove cherry pick state file created by CherryPickCommand, it's not
1001 		// needed for rebase
1002 		repo.writeCherryPickHead(null);
1003 		return RebaseResult.result(status, commitToPick);
1004 	}
1005 
1006 	String toAuthorScript(PersonIdent author) {
1007 		StringBuilder sb = new StringBuilder(100);
1008 		sb.append(GIT_AUTHOR_NAME);
1009 		sb.append("='"); //$NON-NLS-1$
1010 		sb.append(author.getName());
1011 		sb.append("'\n"); //$NON-NLS-1$
1012 		sb.append(GIT_AUTHOR_EMAIL);
1013 		sb.append("='"); //$NON-NLS-1$
1014 		sb.append(author.getEmailAddress());
1015 		sb.append("'\n"); //$NON-NLS-1$
1016 		// the command line uses the "external String"
1017 		// representation for date and timezone
1018 		sb.append(GIT_AUTHOR_DATE);
1019 		sb.append("='"); //$NON-NLS-1$
1020 		sb.append("@"); // @ for time in seconds since 1970 //$NON-NLS-1$
1021 		String externalString = author.toExternalString();
1022 		sb
1023 				.append(externalString.substring(externalString
1024 						.lastIndexOf('>') + 2));
1025 		sb.append("'\n"); //$NON-NLS-1$
1026 		return sb.toString();
1027 	}
1028 
1029 	/**
1030 	 * Removes the number of lines given in the parameter from the
1031 	 * <code>git-rebase-todo</code> file but preserves comments and other lines
1032 	 * that can not be parsed as steps
1033 	 *
1034 	 * @param numSteps
1035 	 * @throws IOException
1036 	 */
1037 	private void popSteps(int numSteps) throws IOException {
1038 		if (numSteps == 0)
1039 			return;
1040 		List<RebaseTodoLine> todoLines = new LinkedList<>();
1041 		List<RebaseTodoLine> poppedLines = new LinkedList<>();
1042 
1043 		for (RebaseTodoLine line : repo.readRebaseTodo(
1044 				rebaseState.getPath(GIT_REBASE_TODO), true)) {
1045 			if (poppedLines.size() >= numSteps
1046 					|| RebaseTodoLine.Action.COMMENT.equals(line.getAction()))
1047 				todoLines.add(line);
1048 			else
1049 				poppedLines.add(line);
1050 		}
1051 
1052 		repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO),
1053 				todoLines, false);
1054 		if (!poppedLines.isEmpty()) {
1055 			repo.writeRebaseTodoFile(rebaseState.getPath(DONE), poppedLines,
1056 					true);
1057 		}
1058 	}
1059 
1060 	private RebaseResult initFilesAndRewind() throws IOException,
1061 			GitAPIException {
1062 		// we need to store everything into files so that we can implement
1063 		// --skip, --continue, and --abort
1064 
1065 		Ref head = getHead();
1066 
1067 		ObjectId headId = head.getObjectId();
1068 		if (headId == null) {
1069 			throw new RefNotFoundException(MessageFormat.format(
1070 					JGitText.get().refNotResolved, Constants.HEAD));
1071 		}
1072 		String headName = getHeadName(head);
1073 		RevCommit headCommit = walk.lookupCommit(headId);
1074 		RevCommit upstream = walk.lookupCommit(upstreamCommit.getId());
1075 
1076 		if (!isInteractive() && walk.isMergedInto(upstream, headCommit))
1077 			return RebaseResult.UP_TO_DATE_RESULT;
1078 		else if (!isInteractive() && walk.isMergedInto(headCommit, upstream)) {
1079 			// head is already merged into upstream, fast-foward
1080 			monitor.beginTask(MessageFormat.format(
1081 					JGitText.get().resettingHead,
1082 					upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
1083 			checkoutCommit(headName, upstreamCommit);
1084 			monitor.endTask();
1085 
1086 			updateHead(headName, upstreamCommit, upstream);
1087 			return RebaseResult.FAST_FORWARD_RESULT;
1088 		}
1089 
1090 		monitor.beginTask(JGitText.get().obtainingCommitsForCherryPick,
1091 				ProgressMonitor.UNKNOWN);
1092 
1093 		// create the folder for the meta information
1094 		FileUtils.mkdir(rebaseState.getDir(), true);
1095 
1096 		repo.writeOrigHead(headId);
1097 		rebaseState.createFile(REBASE_HEAD, headId.name());
1098 		rebaseState.createFile(REBASE_HEAD_LEGACY, headId.name());
1099 		rebaseState.createFile(HEAD_NAME, headName);
1100 		rebaseState.createFile(ONTO, upstreamCommit.name());
1101 		rebaseState.createFile(ONTO_NAME, upstreamCommitName);
1102 		if (isInteractive() || preserveMerges) {
1103 			// --preserve-merges is an interactive mode for native git. Without
1104 			// this, native git rebase --continue after a conflict would fall
1105 			// into merge mode.
1106 			rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
1107 		}
1108 		rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
1109 
1110 		ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<>();
1111 		toDoSteps.add(new RebaseTodoLine("# Created by EGit: rebasing " + headId.name() //$NON-NLS-1$
1112 						+ " onto " + upstreamCommit.name())); //$NON-NLS-1$
1113 		// determine the commits to be applied
1114 		List<RevCommit> cherryPickList = calculatePickList(headCommit);
1115 		ObjectReader reader = walk.getObjectReader();
1116 		for (RevCommit commit : cherryPickList)
1117 			toDoSteps.add(new RebaseTodoLine(Action.PICK, reader
1118 					.abbreviate(commit), commit.getShortMessage()));
1119 		repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO),
1120 				toDoSteps, false);
1121 
1122 		monitor.endTask();
1123 
1124 		// we rewind to the upstream commit
1125 		monitor.beginTask(MessageFormat.format(JGitText.get().rewinding,
1126 				upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
1127 		boolean checkoutOk = false;
1128 		try {
1129 			checkoutOk = checkoutCommit(headName, upstreamCommit);
1130 		} finally {
1131 			if (!checkoutOk)
1132 				FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
1133 		}
1134 		monitor.endTask();
1135 
1136 		return null;
1137 	}
1138 
1139 	private List<RevCommit> calculatePickList(RevCommit headCommit)
1140 			throws GitAPIException, NoHeadException, IOException {
1141 		List<RevCommit> cherryPickList = new ArrayList<>();
1142 		try (RevWalk/RevWalk.html#RevWalk">RevWalk r = new RevWalk(repo)) {
1143 			r.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
1144 			r.sort(RevSort.COMMIT_TIME_DESC, true);
1145 			r.markUninteresting(r.lookupCommit(upstreamCommit));
1146 			r.markStart(r.lookupCommit(headCommit));
1147 			Iterator<RevCommit> commitsToUse = r.iterator();
1148 			while (commitsToUse.hasNext()) {
1149 				RevCommit commit = commitsToUse.next();
1150 				if (preserveMerges || commit.getParentCount() == 1) {
1151 					cherryPickList.add(commit);
1152 				}
1153 			}
1154 		}
1155 		Collections.reverse(cherryPickList);
1156 
1157 		if (preserveMerges) {
1158 			// When preserving merges we only rewrite commits which have at
1159 			// least one parent that is itself rewritten (or a merge base)
1160 			File rewrittenDir = rebaseState.getRewrittenDir();
1161 			FileUtils.mkdir(rewrittenDir, false);
1162 			walk.reset();
1163 			walk.setRevFilter(RevFilter.MERGE_BASE);
1164 			walk.markStart(upstreamCommit);
1165 			walk.markStart(headCommit);
1166 			RevCommit base;
1167 			while ((base = walk.next()) != null)
1168 				RebaseState.createFile(rewrittenDir, base.getName(),
1169 						upstreamCommit.getName());
1170 
1171 			Iterator<RevCommit> iterator = cherryPickList.iterator();
1172 			pickLoop: while(iterator.hasNext()){
1173 				RevCommit commit = iterator.next();
1174 				for (int i = 0; i < commit.getParentCount(); i++) {
1175 					boolean parentRewritten = new File(rewrittenDir, commit
1176 							.getParent(i).getName()).exists();
1177 					if (parentRewritten) {
1178 						new File(rewrittenDir, commit.getName()).createNewFile();
1179 						continue pickLoop;
1180 					}
1181 				}
1182 				// commit is only merged in, needs not be rewritten
1183 				iterator.remove();
1184 			}
1185 		}
1186 		return cherryPickList;
1187 	}
1188 
1189 	private static String getHeadName(Ref head) {
1190 		String headName;
1191 		if (head.isSymbolic()) {
1192 			headName = head.getTarget().getName();
1193 		} else {
1194 			ObjectId headId = head.getObjectId();
1195 			// the callers are checking this already
1196 			assert headId != null;
1197 			headName = headId.getName();
1198 		}
1199 		return headName;
1200 	}
1201 
1202 	private Ref getHead() throws IOException, RefNotFoundException {
1203 		Ref head = repo.exactRef(Constants.HEAD);
1204 		if (head == null || head.getObjectId() == null)
1205 			throw new RefNotFoundException(MessageFormat.format(
1206 					JGitText.get().refNotResolved, Constants.HEAD));
1207 		return head;
1208 	}
1209 
1210 	private boolean isInteractive() {
1211 		return interactiveHandler != null;
1212 	}
1213 
1214 	/**
1215 	 * Check if we can fast-forward and returns the new head if it is possible
1216 	 *
1217 	 * @param newCommit
1218 	 *            a {@link org.eclipse.jgit.revwalk.RevCommit} object to check
1219 	 *            if we can fast-forward to.
1220 	 * @return the new head, or null
1221 	 * @throws java.io.IOException
1222 	 * @throws org.eclipse.jgit.api.errors.GitAPIException
1223 	 */
1224 	public RevCommit/../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit tryFastForward(RevCommit newCommit) throws IOException,
1225 			GitAPIException {
1226 		Ref head = getHead();
1227 
1228 		ObjectId headId = head.getObjectId();
1229 		if (headId == null)
1230 			throw new RefNotFoundException(MessageFormat.format(
1231 					JGitText.get().refNotResolved, Constants.HEAD));
1232 		RevCommit headCommit = walk.lookupCommit(headId);
1233 		if (walk.isMergedInto(newCommit, headCommit))
1234 			return newCommit;
1235 
1236 		String headName = getHeadName(head);
1237 		return tryFastForward(headName, headCommit, newCommit);
1238 	}
1239 
1240 	private RevCommitgit/revwalk/RevCommit.html#RevCommit">RevCommit tryFastForward(String headName, RevCommit oldCommit,
1241 			RevCommit newCommit) throws IOException, GitAPIException {
1242 		boolean tryRebase = false;
1243 		for (RevCommit parentCommit : newCommit.getParents())
1244 			if (parentCommit.equals(oldCommit))
1245 				tryRebase = true;
1246 		if (!tryRebase)
1247 			return null;
1248 
1249 		CheckoutCommand co = new CheckoutCommand(repo);
1250 		try {
1251 			co.setProgressMonitor(monitor);
1252 			co.setName(newCommit.name()).call();
1253 			if (headName.startsWith(Constants.R_HEADS)) {
1254 				RefUpdate rup = repo.updateRef(headName);
1255 				rup.setExpectedOldObjectId(oldCommit);
1256 				rup.setNewObjectId(newCommit);
1257 				rup.setRefLogMessage("Fast-forward from " + oldCommit.name() //$NON-NLS-1$
1258 						+ " to " + newCommit.name(), false); //$NON-NLS-1$
1259 				Result res = rup.update(walk);
1260 				switch (res) {
1261 				case FAST_FORWARD:
1262 				case NO_CHANGE:
1263 				case FORCED:
1264 					break;
1265 				default:
1266 					throw new IOException("Could not fast-forward"); //$NON-NLS-1$
1267 				}
1268 			}
1269 			return newCommit;
1270 		} catch (RefAlreadyExistsException | RefNotFoundException
1271 				| InvalidRefNameException | CheckoutConflictException e) {
1272 			throw new JGitInternalException(e.getMessage(), e);
1273 		}
1274 	}
1275 
1276 	private void checkParameters() throws WrongRepositoryStateException {
1277 		if (this.operation == Operation.PROCESS_STEPS) {
1278 			if (rebaseState.getFile(DONE).exists())
1279 				throw new WrongRepositoryStateException(MessageFormat.format(
1280 						JGitText.get().wrongRepositoryState, repo
1281 								.getRepositoryState().name()));
1282 		}
1283 		if (this.operation != Operation.BEGIN) {
1284 			// these operations are only possible while in a rebasing state
1285 			switch (repo.getRepositoryState()) {
1286 			case REBASING_INTERACTIVE:
1287 			case REBASING:
1288 			case REBASING_REBASING:
1289 			case REBASING_MERGE:
1290 				break;
1291 			default:
1292 				throw new WrongRepositoryStateException(MessageFormat.format(
1293 						JGitText.get().wrongRepositoryState, repo
1294 								.getRepositoryState().name()));
1295 			}
1296 		} else
1297 			switch (repo.getRepositoryState()) {
1298 			case SAFE:
1299 				if (this.upstreamCommit == null)
1300 					throw new JGitInternalException(MessageFormat
1301 							.format(JGitText.get().missingRequiredParameter,
1302 									"upstream")); //$NON-NLS-1$
1303 				return;
1304 			default:
1305 				throw new WrongRepositoryStateException(MessageFormat.format(
1306 						JGitText.get().wrongRepositoryState, repo
1307 								.getRepositoryState().name()));
1308 
1309 			}
1310 	}
1311 
1312 	private RebaseResult/../../../org/eclipse/jgit/api/RebaseResult.html#RebaseResult">RebaseResult abort(RebaseResult result) throws IOException,
1313 			GitAPIException {
1314 		ObjectId origHead = getOriginalHead();
1315 		try {
1316 			String commitId = origHead != null ? origHead.name() : null;
1317 			monitor.beginTask(MessageFormat.format(
1318 					JGitText.get().abortingRebase, commitId),
1319 					ProgressMonitor.UNKNOWN);
1320 
1321 			DirCacheCheckout dco;
1322 			if (commitId == null)
1323 				throw new JGitInternalException(
1324 						JGitText.get().abortingRebaseFailedNoOrigHead);
1325 			ObjectId id = repo.resolve(commitId);
1326 			RevCommit commit = walk.parseCommit(id);
1327 			if (result.getStatus().equals(Status.FAILED)) {
1328 				RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD));
1329 				dco = new DirCacheCheckout(repo, head.getTree(),
1330 						repo.lockDirCache(), commit.getTree());
1331 			} else {
1332 				dco = new DirCacheCheckout(repo, repo.lockDirCache(),
1333 						commit.getTree());
1334 			}
1335 			dco.setFailOnConflict(false);
1336 			dco.checkout();
1337 			walk.close();
1338 		} finally {
1339 			monitor.endTask();
1340 		}
1341 		try {
1342 			String headName = rebaseState.readFile(HEAD_NAME);
1343 				monitor.beginTask(MessageFormat.format(
1344 						JGitText.get().resettingHead, headName),
1345 						ProgressMonitor.UNKNOWN);
1346 
1347 			Result res = null;
1348 			RefUpdate refUpdate = repo.updateRef(Constants.HEAD, false);
1349 			refUpdate.setRefLogMessage("rebase: aborting", false); //$NON-NLS-1$
1350 			if (headName.startsWith(Constants.R_REFS)) {
1351 				// update the HEAD
1352 				res = refUpdate.link(headName);
1353 			} else {
1354 				refUpdate.setNewObjectId(origHead);
1355 				res = refUpdate.forceUpdate();
1356 
1357 			}
1358 			switch (res) {
1359 			case FAST_FORWARD:
1360 			case FORCED:
1361 			case NO_CHANGE:
1362 				break;
1363 			default:
1364 				throw new JGitInternalException(
1365 						JGitText.get().abortingRebaseFailed);
1366 			}
1367 			boolean stashConflicts = autoStashApply();
1368 			// cleanup the files
1369 			FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
1370 			repo.writeCherryPickHead(null);
1371 			repo.writeMergeHeads(null);
1372 			if (stashConflicts)
1373 				return RebaseResult.STASH_APPLY_CONFLICTS_RESULT;
1374 			return result;
1375 
1376 		} finally {
1377 			monitor.endTask();
1378 		}
1379 	}
1380 
1381 	private ObjectId getOriginalHead() throws IOException {
1382 		try {
1383 			return ObjectId.fromString(rebaseState.readFile(REBASE_HEAD));
1384 		} catch (FileNotFoundException e) {
1385 			try {
1386 				return ObjectId
1387 						.fromString(rebaseState.readFile(REBASE_HEAD_LEGACY));
1388 			} catch (FileNotFoundException ex) {
1389 				return repo.readOrigHead();
1390 			}
1391 		}
1392 	}
1393 
1394 	private boolean checkoutCommit(String headName, RevCommit commit)
1395 			throws IOException,
1396 			CheckoutConflictException {
1397 		try {
1398 			RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD));
1399 			DirCacheCheckout dco = new DirCacheCheckout(repo, head.getTree(),
1400 					repo.lockDirCache(), commit.getTree());
1401 			dco.setFailOnConflict(true);
1402 			dco.setProgressMonitor(monitor);
1403 			try {
1404 				dco.checkout();
1405 			} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
1406 				throw new CheckoutConflictException(dco.getConflicts(), cce);
1407 			}
1408 			// update the HEAD
1409 			RefUpdate refUpdate = repo.updateRef(Constants.HEAD, true);
1410 			refUpdate.setExpectedOldObjectId(head);
1411 			refUpdate.setNewObjectId(commit);
1412 			refUpdate.setRefLogMessage(
1413 					"checkout: moving from " //$NON-NLS-1$
1414 							+ Repository.shortenRefName(headName)
1415 							+ " to " + commit.getName(), false); //$NON-NLS-1$
1416 			Result res = refUpdate.forceUpdate();
1417 			switch (res) {
1418 			case FAST_FORWARD:
1419 			case NO_CHANGE:
1420 			case FORCED:
1421 				break;
1422 			default:
1423 				throw new IOException(
1424 						JGitText.get().couldNotRewindToUpstreamCommit);
1425 			}
1426 		} finally {
1427 			walk.close();
1428 			monitor.endTask();
1429 		}
1430 		return true;
1431 	}
1432 
1433 
1434 	/**
1435 	 * Set upstream {@code RevCommit}
1436 	 *
1437 	 * @param upstream
1438 	 *            the upstream commit
1439 	 * @return {@code this}
1440 	 */
1441 	public RebaseCommand setUpstream(RevCommit upstream) {
1442 		this.upstreamCommit = upstream;
1443 		this.upstreamCommitName = upstream.name();
1444 		return this;
1445 	}
1446 
1447 	/**
1448 	 * Set the upstream commit
1449 	 *
1450 	 * @param upstream
1451 	 *            id of the upstream commit
1452 	 * @return {@code this}
1453 	 */
1454 	public RebaseCommand setUpstream(AnyObjectId upstream) {
1455 		try {
1456 			this.upstreamCommit = walk.parseCommit(upstream);
1457 			this.upstreamCommitName = upstream.name();
1458 		} catch (IOException e) {
1459 			throw new JGitInternalException(MessageFormat.format(
1460 					JGitText.get().couldNotReadObjectWhileParsingCommit,
1461 					upstream.name()), e);
1462 		}
1463 		return this;
1464 	}
1465 
1466 	/**
1467 	 * Set the upstream branch
1468 	 *
1469 	 * @param upstream
1470 	 *            the name of the upstream branch
1471 	 * @return {@code this}
1472 	 * @throws org.eclipse.jgit.api.errors.RefNotFoundException
1473 	 */
1474 	public RebaseCommand setUpstream(String upstream)
1475 			throws RefNotFoundException {
1476 		try {
1477 			ObjectId upstreamId = repo.resolve(upstream);
1478 			if (upstreamId == null)
1479 				throw new RefNotFoundException(MessageFormat.format(JGitText
1480 						.get().refNotResolved, upstream));
1481 			upstreamCommit = walk.parseCommit(repo.resolve(upstream));
1482 			upstreamCommitName = upstream;
1483 			return this;
1484 		} catch (IOException ioe) {
1485 			throw new JGitInternalException(ioe.getMessage(), ioe);
1486 		}
1487 	}
1488 
1489 	/**
1490 	 * Optionally override the name of the upstream. If this is used, it has to
1491 	 * come after any {@link #setUpstream} call.
1492 	 *
1493 	 * @param upstreamName
1494 	 *            the name which will be used to refer to upstream in conflicts
1495 	 * @return {@code this}
1496 	 */
1497 	public RebaseCommand setUpstreamName(String upstreamName) {
1498 		if (upstreamCommit == null) {
1499 			throw new IllegalStateException(
1500 					"setUpstreamName must be called after setUpstream."); //$NON-NLS-1$
1501 		}
1502 		this.upstreamCommitName = upstreamName;
1503 		return this;
1504 	}
1505 
1506 	/**
1507 	 * Set the operation to execute during rebase
1508 	 *
1509 	 * @param operation
1510 	 *            the operation to perform
1511 	 * @return {@code this}
1512 	 */
1513 	public RebaseCommand setOperation(Operation operation) {
1514 		this.operation = operation;
1515 		return this;
1516 	}
1517 
1518 	/**
1519 	 * Set progress monitor
1520 	 *
1521 	 * @param monitor
1522 	 *            a progress monitor
1523 	 * @return this instance
1524 	 */
1525 	public RebaseCommand setProgressMonitor(ProgressMonitor monitor) {
1526 		if (monitor == null) {
1527 			monitor = NullProgressMonitor.INSTANCE;
1528 		}
1529 		this.monitor = monitor;
1530 		return this;
1531 	}
1532 
1533 	/**
1534 	 * Enable interactive rebase
1535 	 * <p>
1536 	 * Does not stop after initialization of interactive rebase. This is
1537 	 * equivalent to
1538 	 * {@link org.eclipse.jgit.api.RebaseCommand#runInteractively(InteractiveHandler, boolean)
1539 	 * runInteractively(handler, false)};
1540 	 * </p>
1541 	 *
1542 	 * @param handler
1543 	 *            the
1544 	 *            {@link org.eclipse.jgit.api.RebaseCommand.InteractiveHandler}
1545 	 *            to use
1546 	 * @return this
1547 	 */
1548 	public RebaseCommand runInteractively(InteractiveHandler handler) {
1549 		return runInteractively(handler, false);
1550 	}
1551 
1552 	/**
1553 	 * Enable interactive rebase
1554 	 * <p>
1555 	 * If stopAfterRebaseInteractiveInitialization is {@code true} the rebase
1556 	 * stops after initialization of interactive rebase returning
1557 	 * {@link org.eclipse.jgit.api.RebaseResult#INTERACTIVE_PREPARED_RESULT}
1558 	 * </p>
1559 	 *
1560 	 * @param handler
1561 	 *            the
1562 	 *            {@link org.eclipse.jgit.api.RebaseCommand.InteractiveHandler}
1563 	 *            to use
1564 	 * @param stopAfterRebaseInteractiveInitialization
1565 	 *            if {@code true} the rebase stops after initialization
1566 	 * @return this instance
1567 	 * @since 3.2
1568 	 */
1569 	public RebaseCommand runInteractively(InteractiveHandler handler,
1570 			final boolean stopAfterRebaseInteractiveInitialization) {
1571 		this.stopAfterInitialization = stopAfterRebaseInteractiveInitialization;
1572 		this.interactiveHandler = handler;
1573 		return this;
1574 	}
1575 
1576 	/**
1577 	 * Set the <code>MergeStrategy</code>.
1578 	 *
1579 	 * @param strategy
1580 	 *            The merge strategy to use during this rebase operation.
1581 	 * @return {@code this}
1582 	 * @since 3.4
1583 	 */
1584 	public RebaseCommand setStrategy(MergeStrategy strategy) {
1585 		this.strategy = strategy;
1586 		return this;
1587 	}
1588 
1589 	/**
1590 	 * Whether to preserve merges during rebase
1591 	 *
1592 	 * @param preserve
1593 	 *            {@code true} to re-create merges during rebase. Defaults to
1594 	 *            {@code false}, a flattening rebase.
1595 	 * @return {@code this}
1596 	 * @since 3.5
1597 	 */
1598 	public RebaseCommand setPreserveMerges(boolean preserve) {
1599 		this.preserveMerges = preserve;
1600 		return this;
1601 	}
1602 
1603 	/**
1604 	 * Allows configure rebase interactive process and modify commit message
1605 	 */
1606 	public interface InteractiveHandler {
1607 		/**
1608 		 * Given list of {@code steps} should be modified according to user
1609 		 * rebase configuration
1610 		 * @param steps
1611 		 *            initial configuration of rebase interactive
1612 		 */
1613 		void prepareSteps(List<RebaseTodoLine> steps);
1614 
1615 		/**
1616 		 * Used for editing commit message on REWORD
1617 		 *
1618 		 * @param commit
1619 		 * @return new commit message
1620 		 */
1621 		String modifyCommitMessage(String commit);
1622 	}
1623 
1624 
1625 	PersonIdent parseAuthor(byte[] raw) {
1626 		if (raw.length == 0)
1627 			return null;
1628 
1629 		Map<String, String> keyValueMap = new HashMap<>();
1630 		for (int p = 0; p < raw.length;) {
1631 			int end = RawParseUtils.nextLF(raw, p);
1632 			if (end == p)
1633 				break;
1634 			int equalsIndex = RawParseUtils.next(raw, p, '=');
1635 			if (equalsIndex == end)
1636 				break;
1637 			String key = RawParseUtils.decode(raw, p, equalsIndex - 1);
1638 			String value = RawParseUtils.decode(raw, equalsIndex + 1, end - 2);
1639 			p = end;
1640 			keyValueMap.put(key, value);
1641 		}
1642 
1643 		String name = keyValueMap.get(GIT_AUTHOR_NAME);
1644 		String email = keyValueMap.get(GIT_AUTHOR_EMAIL);
1645 		String time = keyValueMap.get(GIT_AUTHOR_DATE);
1646 
1647 		// the time is saved as <seconds since 1970> <timezone offset>
1648 		int timeStart = 0;
1649 		if (time.startsWith("@")) //$NON-NLS-1$
1650 			timeStart = 1;
1651 		else
1652 			timeStart = 0;
1653 		long when = Long
1654 				.parseLong(time.substring(timeStart, time.indexOf(' '))) * 1000;
1655 		String tzOffsetString = time.substring(time.indexOf(' ') + 1);
1656 		int multiplier = -1;
1657 		if (tzOffsetString.charAt(0) == '+')
1658 			multiplier = 1;
1659 		int hours = Integer.parseInt(tzOffsetString.substring(1, 3));
1660 		int minutes = Integer.parseInt(tzOffsetString.substring(3, 5));
1661 		// this is in format (+/-)HHMM (hours and minutes)
1662 		// we need to convert into minutes
1663 		int tz = (hours * 60 + minutes) * multiplier;
1664 		if (name != null && email != null)
1665 			return new PersonIdent(name, email, when, tz);
1666 		return null;
1667 	}
1668 
1669 	private static class RebaseState {
1670 
1671 		private final File repoDirectory;
1672 		private File dir;
1673 
1674 		public RebaseState(File repoDirectory) {
1675 			this.repoDirectory = repoDirectory;
1676 		}
1677 
1678 		public File getDir() {
1679 			if (dir == null) {
1680 				File rebaseApply = new File(repoDirectory, REBASE_APPLY);
1681 				if (rebaseApply.exists()) {
1682 					dir = rebaseApply;
1683 				} else {
1684 					File rebaseMerge = new File(repoDirectory, REBASE_MERGE);
1685 					dir = rebaseMerge;
1686 				}
1687 			}
1688 			return dir;
1689 		}
1690 
1691 		/**
1692 		 * @return Directory with rewritten commit hashes, usually exists if
1693 		 *         {@link RebaseCommand#preserveMerges} is true
1694 		 **/
1695 		public File getRewrittenDir() {
1696 			return new File(getDir(), REWRITTEN);
1697 		}
1698 
1699 		public String readFile(String name) throws IOException {
1700 			try {
1701 				return readFile(getDir(), name);
1702 			} catch (FileNotFoundException e) {
1703 				if (ONTO_NAME.equals(name)) {
1704 					// Older JGit mistakenly wrote a file "onto-name" instead of
1705 					// "onto_name". Try that wrong name just in case somebody
1706 					// upgraded while a rebase started by JGit was in progress.
1707 					File oldFile = getFile(ONTO_NAME.replace('_', '-'));
1708 					if (oldFile.exists()) {
1709 						return readFile(oldFile);
1710 					}
1711 				}
1712 				throw e;
1713 			}
1714 		}
1715 
1716 		public void createFile(String name, String content) throws IOException {
1717 			createFile(getDir(), name, content);
1718 		}
1719 
1720 		public File getFile(String name) {
1721 			return new File(getDir(), name);
1722 		}
1723 
1724 		public String getPath(String name) {
1725 			return (getDir().getName() + "/" + name); //$NON-NLS-1$
1726 		}
1727 
1728 		private static String readFile(File file) throws IOException {
1729 			byte[] content = IO.readFully(file);
1730 			// strip off the last LF
1731 			int end = RawParseUtils.prevLF(content, content.length);
1732 			return RawParseUtils.decode(content, 0, end + 1);
1733 		}
1734 
1735 		private static String readFile(File directory, String fileName)
1736 				throws IOException {
1737 			return readFile(new File(directory, fileName));
1738 		}
1739 
1740 		private static void createFile(File parentDir, String name,
1741 				String content)
1742 				throws IOException {
1743 			File file = new File(parentDir, name);
1744 			try (FileOutputStream fos = new FileOutputStream(file)) {
1745 				fos.write(content.getBytes(UTF_8));
1746 				fos.write('\n');
1747 			}
1748 		}
1749 
1750 		private static void appendToFile(File file, String content)
1751 				throws IOException {
1752 			try (FileOutputStream fos = new FileOutputStream(file, true)) {
1753 				fos.write(content.getBytes(UTF_8));
1754 				fos.write('\n');
1755 			}
1756 		}
1757 	}
1758 }