aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Aniszczyk <caniszczyk@gmail.com>2011-02-01 08:47:04 -0600
committerChris Aniszczyk <caniszczyk@gmail.com>2011-02-17 10:28:26 -0600
commit5f258d91c02f0876b6dd3fd798717d2fe7c2db18 (patch)
treef98e2fe396cc2d9ef827c8cce34bc644d47e5e55
parentc13bf057540f11e2503e1205d46c800f9d00fd7b (diff)
downloadjgit-5f258d91c02f0876b6dd3fd798717d2fe7c2db18.tar.gz
jgit-5f258d91c02f0876b6dd3fd798717d2fe7c2db18.zip
Add git-reset to the Git API
Bug: 334764 Change-Id: Ice404629687d7f2a595d8d4eccf471b12f7e32ec Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java207
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java258
5 files changed, 480 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
new file mode 100644
index 0000000000..b886432a39
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011, Chris Aniszczyk <caniszczyk@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.api.errors.NoMessageException;
+import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Test;
+
+public class ResetCommandTest extends RepositoryTestCase {
+
+ private Git git;
+
+ private RevCommit initialCommit;
+
+ private File indexFile;
+
+ private File untrackedFile;
+
+ public void setupRepository() throws IOException, NoFilepatternException,
+ NoHeadException, NoMessageException, ConcurrentRefUpdateException,
+ JGitInternalException, WrongRepositoryStateException {
+
+ // create initial commit
+ git = new Git(db);
+ initialCommit = git.commit().setMessage("initial commit").call();
+
+ // create file
+ indexFile = new File(db.getWorkTree(), "a.txt");
+ FileUtils.createNewFile(indexFile);
+ PrintWriter writer = new PrintWriter(indexFile);
+ writer.print("content");
+ writer.flush();
+
+ // add file and commit it
+ git.add().addFilepattern("a.txt").call();
+ git.commit().setMessage("adding a.txt").call();
+
+ // modify file and add to index
+ writer.print("new content");
+ writer.close();
+ git.add().addFilepattern("a.txt").call();
+
+ // create a file not added to the index
+ untrackedFile = new File(db.getWorkTree(),
+ "notAddedToIndex.txt");
+ FileUtils.createNewFile(untrackedFile);
+ PrintWriter writer2 = new PrintWriter(untrackedFile);
+ writer2.print("content");
+ writer2.close();
+ }
+
+ @Test
+ public void testHardReset() throws JGitInternalException,
+ AmbiguousObjectException, IOException, NoFilepatternException,
+ NoHeadException, NoMessageException, ConcurrentRefUpdateException,
+ WrongRepositoryStateException {
+ setupRepository();
+ git.reset().setMode(ResetType.HARD).setRef(initialCommit.getName())
+ .call();
+ // check if HEAD points to initial commit now
+ ObjectId head = db.resolve(Constants.HEAD);
+ assertTrue(head.equals(initialCommit));
+ // check if files were removed
+ assertFalse(indexFile.exists());
+ assertTrue(untrackedFile.exists());
+ // fileInIndex must no longer be in HEAD and in the index
+ String fileInIndexPath = indexFile.getAbsolutePath();
+ assertFalse(inHead(fileInIndexPath));
+ assertFalse(inIndex(indexFile.getName()));
+ }
+
+ @Test
+ public void testSoftReset() throws JGitInternalException,
+ AmbiguousObjectException, IOException, NoFilepatternException,
+ NoHeadException, NoMessageException, ConcurrentRefUpdateException,
+ WrongRepositoryStateException {
+ setupRepository();
+ git.reset().setMode(ResetType.SOFT).setRef(initialCommit.getName())
+ .call();
+ // check if HEAD points to initial commit now
+ ObjectId head = db.resolve(Constants.HEAD);
+ assertTrue(head.equals(initialCommit));
+ // check if files still exist
+ assertTrue(untrackedFile.exists());
+ assertTrue(indexFile.exists());
+ // fileInIndex must no longer be in HEAD but has to be in the index
+ String fileInIndexPath = indexFile.getAbsolutePath();
+ assertFalse(inHead(fileInIndexPath));
+ assertTrue(inIndex(indexFile.getName()));
+ }
+
+ @Test
+ public void testMixedReset() throws JGitInternalException,
+ AmbiguousObjectException, IOException, NoFilepatternException,
+ NoHeadException, NoMessageException, ConcurrentRefUpdateException,
+ WrongRepositoryStateException {
+ setupRepository();
+ git.reset().setMode(ResetType.MIXED).setRef(initialCommit.getName())
+ .call();
+ // check if HEAD points to initial commit now
+ ObjectId head = db.resolve(Constants.HEAD);
+ assertTrue(head.equals(initialCommit));
+ // check if files still exist
+ assertTrue(untrackedFile.exists());
+ assertTrue(indexFile.exists());
+ // fileInIndex must no longer be in HEAD and in the index
+ String fileInIndexPath = indexFile.getAbsolutePath();
+ assertFalse(inHead(fileInIndexPath));
+ assertFalse(inIndex(indexFile.getName()));
+ }
+
+ /**
+ * Checks if a file with the given path exists in the HEAD tree
+ *
+ * @param path
+ * @return true if the file exists
+ * @throws IOException
+ */
+ private boolean inHead(String path) throws IOException {
+ ObjectId headId = db.resolve(Constants.HEAD);
+ RevWalk rw = new RevWalk(db);
+ TreeWalk tw = null;
+ try {
+ tw = TreeWalk.forPath(db, path, rw.parseTree(headId));
+ return tw != null;
+ } finally {
+ rw.release();
+ rw.dispose();
+ if (tw != null)
+ tw.release();
+ }
+ }
+
+ /**
+ * Checks if a file with the given path exists in the index
+ *
+ * @param path
+ * @return true if the file exists
+ * @throws IOException
+ */
+ private boolean inIndex(String path) throws IOException {
+ DirCache dc = DirCache.read(db.getIndexFile(), db.getFS());
+ return dc.getEntry(path) != null;
+ }
+
+}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index e48638397d..b2d9937da0 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -175,6 +175,7 @@ exceptionCaughtDuringExecutionOfFetchCommand=Exception caught during execution o
exceptionCaughtDuringExecutionOfMergeCommand=Exception caught during execution of merge command. {0}
exceptionCaughtDuringExecutionOfPushCommand=Exception caught during execution of push command
exceptionCaughtDuringExecutionOfPullCommand=Exception caught during execution of pull command
+exceptionCaughtDuringExecutionOfResetCommand=Exception caught during execution of reset command. {0}
exceptionCaughtDuringExecutionOfRevertCommand=Exception caught during execution of revert command. {0}
exceptionCaughtDuringExecutionOfRmCommand=Exception caught during execution of rm command
exceptionCaughtDuringExecutionOfTagCommand=Exception caught during execution of tag command
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index 7276dd26fb..7265db8bc6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -235,6 +235,7 @@ public class JGitText extends TranslationBundle {
/***/ public String exceptionCaughtDuringExecutionOfMergeCommand;
/***/ public String exceptionCaughtDuringExecutionOfPushCommand;
/***/ public String exceptionCaughtDuringExecutionOfPullCommand;
+ /***/ public String exceptionCaughtDuringExecutionOfResetCommand;
/***/ public String exceptionCaughtDuringExecutionOfRevertCommand;
/***/ public String exceptionCaughtDuringExecutionOfRmCommand;
/***/ public String exceptionCaughtDuringExecutionOfTagCommand;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 27045eb47d..88643f1702 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -322,6 +322,19 @@ public class Git {
}
/**
+ * Returns a command object to execute a {@code reset} command
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-reset.html"
+ * >Git documentation about reset</a>
+ * @return a {@link ResetCommand} used to collect all optional parameters
+ * and to finally execute the {@code reset} command
+ */
+ public ResetCommand reset() {
+ return new ResetCommand(repo);
+ }
+
+ /**
* @return the git repository this class is interacting with
*/
public Repository getRepository() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
new file mode 100644
index 0000000000..d55767d3a6
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011, Chris Aniszczyk <caniszczyk@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/**
+ * A class used to execute a {@code Reset} command. It has setters for all
+ * supported options and arguments of this command and a {@link #call()} method
+ * to finally execute the command. Each instance of this class should only be
+ * used for one invocation of the command (means: one call to {@link #call()})
+ *
+ * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-reset.html"
+ * >Git documentation about Reset</a>
+ */
+public class ResetCommand extends GitCommand<Ref> {
+
+ /**
+ * Kind of reset
+ */
+ public enum ResetType {
+ /**
+ * Just change the ref, the index and workdir are not changed.
+ */
+ SOFT,
+
+ /**
+ * Change the ref and the index, the workdir is not changed.
+ */
+ MIXED,
+
+ /**
+ * Change the ref, the index and the workdir
+ */
+ HARD,
+
+ /**
+ * Resets the index and updates the files in the working tree that are
+ * different between respective commit and HEAD, but keeps those which
+ * are different between the index and working tree
+ */
+ MERGE, // TODO not implemented yet
+
+ /**
+ * Change the ref, the index and the workdir that are different between
+ * respective commit and HEAD
+ */
+ KEEP // TODO not implemented yet
+ }
+
+ private String ref;
+
+ private ResetType mode;
+
+ /**
+ *
+ * @param repo
+ */
+ public ResetCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * Executes the {@code Reset} command. Each instance of this class should
+ * only be used for one invocation of the command. Don't call this method
+ * twice on an instance.
+ *
+ * @return the Ref after reset
+ */
+ public Ref call() throws IOException {
+ checkCallable();
+
+ Ref r;
+ RevCommit commit;
+
+ try {
+ boolean merging = false;
+ if (repo.getRepositoryState().equals(RepositoryState.MERGING)
+ || repo.getRepositoryState().equals(
+ RepositoryState.MERGING_RESOLVED))
+ merging = true;
+
+ // resolve the ref to a commit
+ final ObjectId commitId;
+ try {
+ commitId = repo.resolve(ref);
+ } catch (IOException e) {
+ throw new JGitInternalException(
+ MessageFormat.format(JGitText.get().cannotRead, ref),
+ e);
+ }
+ RevWalk rw = new RevWalk(repo);
+ try {
+ commit = rw.parseCommit(commitId);
+ } catch (IOException e) {
+ throw new JGitInternalException(
+ MessageFormat.format(
+ JGitText.get().cannotReadCommit, commitId.toString()),
+ e);
+ } finally {
+ rw.release();
+ }
+
+ // write the ref
+ final RefUpdate ru = repo.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commitId);
+
+ String refName = Repository.shortenRefName(ref);
+ String message = "reset --" //$NON-NLS-1$
+ + mode.toString().toLowerCase() + " " + refName; //$NON-NLS-1$
+ ru.setRefLogMessage(message, false);
+ if (ru.forceUpdate() == RefUpdate.Result.LOCK_FAILURE)
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().cannotLock, ru.getName()));
+
+ switch (mode) {
+ case HARD:
+ checkoutIndex(commit);
+ break;
+ case MIXED:
+ resetIndex(commit);
+ break;
+ case SOFT: // do nothing, only the ref was changed
+ break;
+ case KEEP: // TODO
+ case MERGE: // TODO
+ throw new UnsupportedOperationException();
+
+ }
+
+ if (mode != ResetType.SOFT && merging)
+ resetMerge();
+
+ setCallable(false);
+ r = ru.getRef();
+ } catch (IOException e) {
+ throw new JGitInternalException(
+ JGitText.get().exceptionCaughtDuringExecutionOfResetCommand,
+ e);
+ }
+
+ return r;
+ }
+
+ /**
+ * @param ref
+ * the ref to reset to
+ * @return this instance
+ */
+ public ResetCommand setRef(String ref) {
+ this.ref = ref;
+ return this;
+ }
+
+ /**
+ * @param mode
+ * the mode of the reset command
+ * @return this instance
+ */
+ public ResetCommand setMode(ResetType mode) {
+ this.mode = mode;
+ return this;
+ }
+
+ private void resetIndex(RevCommit commit) throws IOException {
+ DirCache dc = null;
+ try {
+ dc = repo.lockDirCache();
+ dc.clear();
+ DirCacheBuilder dcb = dc.builder();
+ dcb.addTree(new byte[0], 0, repo.newObjectReader(),
+ commit.getTree());
+ dcb.commit();
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ if (dc != null)
+ dc.unlock();
+ }
+ }
+
+ private void checkoutIndex(RevCommit commit) throws IOException {
+ DirCache dc = null;
+ try {
+ dc = repo.lockDirCache();
+ DirCacheCheckout checkout = new DirCacheCheckout(repo, dc,
+ commit.getTree());
+ checkout.setFailOnConflict(false);
+ checkout.checkout();
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ if (dc != null)
+ dc.unlock();
+ }
+ }
+
+ private void resetMerge() throws IOException {
+ repo.writeMergeHeads(null);
+ repo.writeMergeCommitMsg(null);
+ }
+
+}