diff options
author | Robin Stocker <robin@nibor.org> | 2011-04-03 19:37:55 +0200 |
---|---|---|
committer | Chris Aniszczyk <caniszczyk@gmail.com> | 2011-04-06 13:28:10 -0500 |
commit | 6e10aa42e90a25b82f00f0c27574f57ffa9e4a25 (patch) | |
tree | e2bf294858e9a16220df5ab54508d94267c14109 /org.eclipse.jgit | |
parent | fbf35fea4ec254339f9b0eee7865eb6ccfe22700 (diff) | |
download | jgit-6e10aa42e90a25b82f00f0c27574f57ffa9e4a25.tar.gz jgit-6e10aa42e90a25b82f00f0c27574f57ffa9e4a25.zip |
Add CHERRY_PICK_HEAD for cherry-pick conflicts
Add handling of CHERRY_PICK_HEAD file in .git (similar to MERGE_HEAD),
which is written in case of a conflicting cherry-pick merge.
It is used so that Repository.getRepositoryState can return the new
states CHERRY_PICKING and CHERRY_PICKING_RESOLVED. These states, as well
as CHERRY_PICK_HEAD can be used in EGit to properly show the merge tool.
Also, in case of a conflict, MERGE_MSG is written with the original
commit message and a "Conflicts" section appended. This way, the
cherry-picked message is not lost and can later be re-used in the commit
dialog.
Bug: 339092
Change-Id: I947967fdc2f1d55016c95106b104c2afcc9797a1
Signed-off-by: Robin Stocker <robin@nibor.org>
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
Diffstat (limited to 'org.eclipse.jgit')
9 files changed, 151 insertions, 10 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java index 1ececd23f0..c8b7e89f1b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -60,6 +60,7 @@ import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.MergeMessageFormatter; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ResolveMerger; import org.eclipse.jgit.revwalk.RevCommit; @@ -150,7 +151,15 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> { if (merger.failed()) return new CherryPickResult(merger.getFailingPaths()); - // merge conflicts + // there are merge conflicts + + String message = new MergeMessageFormatter() + .formatWithConflicts(srcCommit.getFullMessage(), + merger.getUnmergedPaths()); + + repo.writeCherryPickHead(srcCommit.getId()); + repo.writeMergeCommitMsg(message); + return CherryPickResult.CONFLICT; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java index 963e3ed842..2412e2c6c0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -233,6 +233,9 @@ public class CommitCommand extends GitCommand<RevCommit> { // used for merge commits repo.writeMergeCommitMsg(null); repo.writeMergeHeads(null); + } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) { + repo.writeMergeCommitMsg(null); + repo.writeCherryPickHead(null); } return revCommit; } 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 cf2111ea7e..7e2e677993 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -399,6 +399,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> { Constants.CHARACTER_ENCODING)); createFile(rebaseDir, STOPPED_SHA, repo.newObjectReader().abbreviate( commitToPick).name()); + // Remove cherry pick state file created by CherryPickCommand, it's not + // needed for rebase + repo.writeCherryPickHead(null); return new RebaseResult(commitToPick); } @@ -744,6 +747,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> { } // cleanup the files FileUtils.delete(rebaseDir, FileUtils.RECURSIVE); + repo.writeCherryPickHead(null); return result; } finally { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java index d55767d3a6..24aae22ffd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java @@ -129,11 +129,12 @@ public class ResetCommand extends GitCommand<Ref> { RevCommit commit; try { - boolean merging = false; - if (repo.getRepositoryState().equals(RepositoryState.MERGING) - || repo.getRepositoryState().equals( - RepositoryState.MERGING_RESOLVED)) - merging = true; + RepositoryState state = repo.getRepositoryState(); + final boolean merging = state.equals(RepositoryState.MERGING) + || state.equals(RepositoryState.MERGING_RESOLVED); + final boolean cherryPicking = state + .equals(RepositoryState.CHERRY_PICKING) + || state.equals(RepositoryState.CHERRY_PICKING_RESOLVED); // resolve the ref to a commit final ObjectId commitId; @@ -183,8 +184,12 @@ public class ResetCommand extends GitCommand<Ref> { } - if (mode != ResetType.SOFT && merging) - resetMerge(); + if (mode != ResetType.SOFT) { + if (merging) + resetMerge(); + else if (cherryPicking) + resetCherryPick(); + } setCallable(false); r = ru.getRef(); @@ -255,4 +260,9 @@ public class ResetCommand extends GitCommand<Ref> { repo.writeMergeCommitMsg(null); } + private void resetCherryPick() throws IOException { + repo.writeCherryPickHead(null); + repo.writeMergeCommitMsg(null); + } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java index 0290689c81..1d77cc174f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -536,6 +536,9 @@ public final class Constants { /** name of the file containing the IDs of the parents of a merge commit */ public static final String MERGE_HEAD = "MERGE_HEAD"; + /** name of the file containing the ID of a cherry pick commit in case of conflicts */ + public static final String CHERRY_PICK_HEAD = "CHERRY_PICK_HEAD"; + /** * name of the ref ORIG_HEAD used by certain commands to store the original * value of HEAD diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 759ab3e3a2..4847a5dbf5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -922,7 +922,7 @@ public abstract class Repository { return RepositoryState.REBASING_MERGE; // Both versions - if (new File(getDirectory(), "MERGE_HEAD").exists()) { + if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) { // we are merging - now check whether we have unmerged paths try { if (!readDirCache().hasUnmergedPaths()) { @@ -941,6 +941,20 @@ public abstract class Repository { if (new File(getDirectory(), "BISECT_LOG").exists()) return RepositoryState.BISECTING; + if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) { + try { + if (!readDirCache().hasUnmergedPaths()) { + // no unmerged paths + return RepositoryState.CHERRY_PICKING_RESOLVED; + } + } catch (IOException e) { + // fall through to CHERRY_PICKING + e.printStackTrace(); + } + + return RepositoryState.CHERRY_PICKING; + } + return RepositoryState.SAFE; } @@ -1192,4 +1206,60 @@ public abstract class Repository { FileUtils.delete(mergeHeadFile); } } + + /** + * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD. + * + * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file + * doesn't exist. Also if the file exists but is empty {@code null} + * will be returned + * @throws IOException + * @throws NoWorkTreeException + * if this is bare, which implies it has no working directory. + * See {@link #isBare()}. + */ + public ObjectId readCherryPickHead() throws IOException, + NoWorkTreeException { + if (isBare() || getDirectory() == null) + throw new NoWorkTreeException(); + + File mergeHeadFile = new File(getDirectory(), + Constants.CHERRY_PICK_HEAD); + byte[] raw; + try { + raw = IO.readFully(mergeHeadFile); + } catch (FileNotFoundException notFound) { + return null; + } + + if (raw.length == 0) + return null; + + return ObjectId.fromString(raw, 0); + } + + /** + * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in + * case of conflicts to store the cherry which was tried to be picked. + * + * @param head + * an object id of the cherry commit or <code>null</code> to + * delete the file + * @throws IOException + */ + public void writeCherryPickHead(ObjectId head) throws IOException { + File cherryPickHeadFile = new File(gitDir, Constants.CHERRY_PICK_HEAD); + if (head != null) { + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(cherryPickHeadFile)); + try { + head.copyTo(bos); + bos.write('\n'); + } finally { + bos.close(); + } + } else { + FileUtils.delete(cherryPickHeadFile, FileUtils.SKIP_MISSING); + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java index e0b5389ac0..10170624b1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java @@ -93,6 +93,26 @@ public enum RepositoryState { public String getDescription() { return JGitText.get().repositoryState_merged; } }, + /** An unfinished cherry-pick. Must resolve or reset before continuing normally + */ + CHERRY_PICKING { + public boolean canCheckout() { return false; } + public boolean canResetHead() { return true; } + public boolean canCommit() { return false; } + public String getDescription() { return JGitText.get().repositoryState_conflicts; } + }, + + /** + * A cherry-pick where all conflicts have been resolved. The index does not + * contain any unmerged paths. + */ + CHERRY_PICKING_RESOLVED { + public boolean canCheckout() { return true; } + public boolean canResetHead() { return true; } + public boolean canCommit() { return true; } + public String getDescription() { return JGitText.get().repositoryState_merged; } + }, + /** * An unfinished rebase or am. Must resolve, skip or abort before normal work can take place */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java index cdd7a2f371..96395d0bfa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java @@ -123,6 +123,27 @@ public class MergeMessageFormatter { return sb.toString(); } + /** + * Add section with conflicting paths to merge message. + * + * @param message + * the original merge message + * @param conflictingPaths + * the paths with conflicts + * @return merge message with conflicting paths added + */ + public String formatWithConflicts(String message, + List<String> conflictingPaths) { + StringBuilder sb = new StringBuilder(message); + if (!message.endsWith("\n")) + sb.append("\n"); + sb.append("\n"); + sb.append("Conflicts:\n"); + for (String conflictingPath : conflictingPaths) + sb.append('\t').append(conflictingPath).append('\n'); + return sb.toString(); + } + private static String joinNames(List<String> names, String singular, String plural) { if (names.size() == 1) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java index 5a36a71ee4..bba634a6b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/RefDirectory.java @@ -130,7 +130,8 @@ public class RefDirectory extends RefDatabase { /** The names of the additional refs supported by this class */ private static final String[] additionalRefsNames = new String[] { - Constants.MERGE_HEAD, Constants.FETCH_HEAD, Constants.ORIG_HEAD }; + Constants.MERGE_HEAD, Constants.FETCH_HEAD, Constants.ORIG_HEAD, + Constants.CHERRY_PICK_HEAD }; private final FileRepository parent; |