diff options
author | Stefan Lay <stefan.lay@sap.com> | 2013-12-02 17:24:09 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2013-12-04 22:02:22 +0100 |
commit | f86a488e32906593903acb31a93a82bed8d87915 (patch) | |
tree | 377792666836715cee1ec2f4d5582641d854dce3 /org.eclipse.jgit/src | |
parent | ba0f50d7d34a0a3d47f53139ba78de04b3ebdff6 (diff) | |
download | jgit-f86a488e32906593903acb31a93a82bed8d87915.tar.gz jgit-f86a488e32906593903acb31a93a82bed8d87915.zip |
Implement rebase.autostash
This feature was introduced in native git with version 1.8.4.
Bug: 422951
Change-Id: I42f194174d64d7ada6631e2156c2a7bf93b5e91c
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit/src')
5 files changed, 139 insertions, 23 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java index 55cf001c6c..10b273a744 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -70,6 +70,7 @@ import org.eclipse.jgit.api.errors.NoHeadException; import org.eclipse.jgit.api.errors.NoMessageException; import org.eclipse.jgit.api.errors.RefAlreadyExistsException; import org.eclipse.jgit.api.errors.RefNotFoundException; +import org.eclipse.jgit.api.errors.StashApplyFailureException; import org.eclipse.jgit.api.errors.UnmergedPathsException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.diff.DiffFormatter; @@ -79,6 +80,7 @@ import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AbbreviatedObjectId; import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; @@ -158,6 +160,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> { private static final String MESSAGE_SQUASH = "message-squash"; //$NON-NLS-1$ + private static final String AUTOSTASH = "autostash"; //$NON-NLS-1$ + + private static final String AUTOSTASH_MSG = "On {0}: autostash"; + /** * The available operations */ @@ -257,6 +263,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { .resolve(upstreamCommitId)); break; case BEGIN: + autoStash(); if (stopAfterInitialization || !walk.isMergedInto( walk.parseCommit(repo.resolve(Constants.HEAD)), @@ -272,8 +279,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> { RebaseResult res = initFilesAndRewind(); if (stopAfterInitialization) return RebaseResult.INTERACTIVE_PREPARED_RESULT; - if (res != null) + if (res != null) { + autoStashApply(); return res; + } } if (monitor.isCancelled()) @@ -339,6 +348,57 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } } + private void autoStash() throws GitAPIException, IOException { + if (repo.getConfig().getBoolean(ConfigConstants.CONFIG_REBASE_SECTION, + ConfigConstants.CONFIG_KEY_AUTOSTASH, false)) { + String message = MessageFormat.format( + AUTOSTASH_MSG, + Repository + .shortenRefName(getHeadName(getHead()))); + RevCommit stashCommit = Git.wrap(repo).stashCreate().setRef(null) + .setWorkingDirectoryMessage( + message) + .call(); + if (stashCommit != null) { + FileUtils.mkdir(rebaseState.getDir()); + rebaseState.createFile(AUTOSTASH, stashCommit.getName()); + } + } + } + + private boolean autoStashApply() throws IOException, GitAPIException { + boolean conflicts = false; + if (rebaseState.getFile(AUTOSTASH).exists()) { + String stash = rebaseState.readFile(AUTOSTASH); + try { + Git.wrap(repo).stashApply().setStashRef(stash) + .ignoreRepositoryState(true).call(); + } catch (StashApplyFailureException e) { + conflicts = true; + RevWalk rw = new RevWalk(repo); + ObjectId stashId = repo.resolve(stash); + RevCommit commit = rw.parseCommit(stashId); + updateStashRef(commit, commit.getAuthorIdent(), + commit.getShortMessage()); + } + } + return conflicts; + } + + private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent, + String refLogMessage) throws IOException { + Ref currentRef = repo.getRef(Constants.R_STASH); + RefUpdate refUpdate = repo.updateRef(Constants.R_STASH); + refUpdate.setNewObjectId(commitId); + refUpdate.setRefLogIdent(refLogIdent); + refUpdate.setRefLogMessage(refLogMessage, false); + if (currentRef != null) + refUpdate.setExpectedOldObjectId(currentRef.getObjectId()); + else + refUpdate.setExpectedOldObjectId(ObjectId.zeroId()); + refUpdate.forceUpdate(); + } + private RebaseResult processStep(RebaseTodoLine step, boolean shouldPick) throws IOException, GitAPIException { if (Action.COMMENT.equals(step.getAction())) @@ -432,10 +492,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } private RebaseResult finishRebase(RevCommit newHead, - boolean lastStepWasForward) throws IOException { + boolean lastStepWasForward) throws IOException, GitAPIException { String headName = rebaseState.readFile(HEAD_NAME); updateHead(headName, newHead, upstreamCommit); + boolean stashConflicts = autoStashApply(); FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); + if (stashConflicts) + return RebaseResult.STASH_APPLY_CONFLICTS_RESULT; if (lastStepWasForward || newHead == null) return RebaseResult.FAST_FORWARD_RESULT; return RebaseResult.OK_RESULT; @@ -809,16 +872,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> { // we need to store everything into files so that we can implement // --skip, --continue, and --abort - Ref head = repo.getRef(Constants.HEAD); - if (head == null || head.getObjectId() == null) - throw new RefNotFoundException(MessageFormat.format( - JGitText.get().refNotResolved, Constants.HEAD)); + Ref head = getHead(); - String headName; - if (head.isSymbolic()) - headName = head.getTarget().getName(); - else - headName = head.getObjectId().getName(); + String headName = getHeadName(head); ObjectId headId = head.getObjectId(); if (headId == null) throw new RefNotFoundException(MessageFormat.format( @@ -857,7 +913,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { Collections.reverse(cherryPickList); // create the folder for the meta information - FileUtils.mkdir(rebaseState.getDir()); + FileUtils.mkdir(rebaseState.getDir(), true); repo.writeOrigHead(headId); rebaseState.createFile(REBASE_HEAD, headId.name()); @@ -893,6 +949,23 @@ public class RebaseCommand extends GitCommand<RebaseResult> { return null; } + private static String getHeadName(Ref head) { + String headName; + if (head.isSymbolic()) + headName = head.getTarget().getName(); + else + headName = head.getObjectId().getName(); + return headName; + } + + private Ref getHead() throws IOException, RefNotFoundException { + Ref head = repo.getRef(Constants.HEAD); + if (head == null || head.getObjectId() == null) + throw new RefNotFoundException(MessageFormat.format( + JGitText.get().refNotResolved, Constants.HEAD)); + return head; + } + private boolean isInteractive() { return interactiveHandler != null; } @@ -907,10 +980,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { */ public RevCommit tryFastForward(RevCommit newCommit) throws IOException, GitAPIException { - Ref head = repo.getRef(Constants.HEAD); - if (head == null || head.getObjectId() == null) - throw new RefNotFoundException(MessageFormat.format( - JGitText.get().refNotResolved, Constants.HEAD)); + Ref head = getHead(); ObjectId headId = head.getObjectId(); if (headId == null) @@ -920,11 +990,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { if (walk.isMergedInto(newCommit, headCommit)) return newCommit; - String headName; - if (head.isSymbolic()) - headName = head.getTarget().getName(); - else - headName = head.getObjectId().getName(); + String headName = getHeadName(head); return tryFastForward(headName, headCommit, newCommit); } @@ -1004,7 +1070,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } } - private RebaseResult abort(RebaseResult result) throws IOException { + private RebaseResult abort(RebaseResult result) throws IOException, + GitAPIException { try { ObjectId origHead = repo.readOrigHead(); String commitId = origHead != null ? origHead.name() : null; @@ -1053,9 +1120,12 @@ public class RebaseCommand extends GitCommand<RebaseResult> { JGitText.get().abortingRebaseFailed); } } + boolean stashConflicts = autoStashApply(); // cleanup the files FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); repo.writeCherryPickHead(null); + if (stashConflicts) + return RebaseResult.STASH_APPLY_CONFLICTS_RESULT; return result; } finally { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java index 26d040342e..92c1347ab2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseResult.java @@ -165,6 +165,18 @@ public class RebaseResult { public boolean isSuccessful() { return false; } + }, + + /** + * Applying stash resulted in conflicts + * + * @since 3.2 + */ + STASH_APPLY_CONFLICTS { + @Override + public boolean isSuccessful() { + return true; + } }; /** @@ -189,6 +201,9 @@ public class RebaseResult { static final RebaseResult INTERACTIVE_PREPARED_RESULT = new RebaseResult( Status.INTERACTIVE_PREPARED); + static final RebaseResult STASH_APPLY_CONFLICTS_RESULT = new RebaseResult( + Status.STASH_APPLY_CONFLICTS); + private final Status status; private final RevCommit currentCommit; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java index 73d64529b3..8440d8b950 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java @@ -90,6 +90,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> { private boolean applyIndex = true; + private boolean ignoreRepositoryState; + /** * Create command to apply the changes of a stashed commit * @@ -113,6 +115,16 @@ public class StashApplyCommand extends GitCommand<ObjectId> { return this; } + /** + * @param ignoreRepositoryState + * @return {@code this} + * @since 3.2 + */ + public StashApplyCommand ignoreRepositoryState(boolean ignoreRepositoryState) { + this.ignoreRepositoryState = ignoreRepositoryState; + return this; + } + private ObjectId getStashId() throws GitAPIException { final String revision = stashRef != null ? stashRef : DEFAULT_REF; final ObjectId stashId; @@ -143,7 +155,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> { StashApplyFailureException { checkCallable(); - if (repo.getRepositoryState() != RepositoryState.SAFE) + if (!ignoreRepositoryState + && repo.getRepositoryState() != RepositoryState.SAFE) throw new WrongRepositoryStateException(MessageFormat.format( JGitText.get().stashApplyOnUnsafeRepository, repo.getRepositoryState())); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java index fc21b919b6..cf0b6d1d9c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java @@ -154,6 +154,7 @@ public class StashCreateCommand extends GitCommand<RevCommit> { /** * Set the reference to update with the stashed commit id + * If null, no reference is updated * <p> * This value defaults to {@link Constants#R_STASH} * @@ -185,6 +186,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> { private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent, String refLogMessage) throws IOException { + if (ref == null) + return; Ref currentRef = repo.getRef(ref); RefUpdate refUpdate = repo.updateRef(ref); refUpdate.setNewObjectId(commitId); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 3ff4eefb1c..fd22764b6a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -77,6 +77,13 @@ public class ConfigConstants { /** The "submodule" section */ public static final String CONFIG_SUBMODULE_SECTION = "submodule"; + /** + * The "rebase" section + * + * @since 3.2 + */ + public static final String CONFIG_REBASE_SECTION = "rebase"; + /** The "gc" section */ public static final String CONFIG_GC_SECTION = "gc"; @@ -136,6 +143,14 @@ public class ConfigConstants { /** The "autosetuprebase" key */ public static final String CONFIG_KEY_AUTOSETUPREBASE = "autosetuprebase"; + + /** + * The "autostash" key + * + * @since 3.2 + */ + public static final String CONFIG_KEY_AUTOSTASH = "autostash"; + /** The "name" key */ public static final String CONFIG_KEY_NAME = "name"; |