summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java214
3 files changed, 215 insertions, 3 deletions
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index b2d9937da0..f96d4261cc 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -153,6 +153,7 @@ duplicateRef=Duplicate ref: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
duplicateStagesNotAllowed=Duplicate stages not allowed
eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
+emptyCommit=No changes
emptyPathNotPermitted=Empty path not permitted.
encryptionError=Encryption error: {0}
endOfFileInEscape=End of file in escape
@@ -207,6 +208,7 @@ hunkBelongsToAnotherFile=Hunk belongs to another file
hunkDisconnectedFromFile=Hunk disconnected from file
hunkHeaderDoesNotMatchBodyLineCountOf=Hunk header {0} does not match body line count of {1}
illegalArgumentNotA=Not {0}
+illegalCombinationOfArguments=The combination of arguments {0} and {1} is not allowed
illegalStateExists=exists {0}
improperlyPaddedBase64Input=Improperly padded Base64 input.
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index 7265db8bc6..9ef2cd93ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -213,6 +213,7 @@ public class JGitText extends TranslationBundle {
/***/ public String duplicateRemoteRefUpdateIsIllegal;
/***/ public String duplicateStagesNotAllowed;
/***/ public String eitherGitDirOrWorkTreeRequired;
+ /***/ public String emptyCommit;
/***/ public String emptyPathNotPermitted;
/***/ public String encryptionError;
/***/ public String endOfFileInEscape;
@@ -267,6 +268,7 @@ public class JGitText extends TranslationBundle {
/***/ public String hunkDisconnectedFromFile;
/***/ public String hunkHeaderDoesNotMatchBodyLineCountOf;
/***/ public String illegalArgumentNotA;
+ /***/ public String illegalCombinationOfArguments;
/***/ public String illegalStateExists;
/***/ public String improperlyPaddedBase64Input;
/***/ public String inMemoryBufferLimitExceeded;
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 8ebee49673..8d9ce98187 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -43,7 +43,9 @@
package org.eclipse.jgit.api;
import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -55,6 +57,12 @@ 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.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -68,6 +76,9 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
/**
* A class used to execute a {@code Commit} command. It has setters for all
@@ -87,6 +98,10 @@ public class CommitCommand extends GitCommand<RevCommit> {
private boolean all;
+ private List<String> only = new ArrayList<String>();
+
+ private boolean[] onlyProcessed;
+
private boolean amend;
/**
@@ -170,6 +185,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
// lock the index
DirCache index = repo.lockDirCache();
try {
+ if (!only.isEmpty())
+ index = createTemporaryIndex(headId, index);
+
ObjectInserter odi = repo.newObjectInserter();
try {
// Write the index as tree to the object database. This may
@@ -241,6 +259,165 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
}
+ private DirCache createTemporaryIndex(ObjectId headId, DirCache index)
+ throws IOException {
+ ObjectInserter inserter = null;
+
+ // get DirCacheEditor to modify the index if required
+ DirCacheEditor dcEditor = index.editor();
+
+ // get DirCacheBuilder for newly created in-core index to build a
+ // temporary index for this commit
+ DirCache inCoreIndex = DirCache.newInCore();
+ DirCacheBuilder dcBuilder = inCoreIndex.builder();
+
+ onlyProcessed = new boolean[only.size()];
+ boolean emptyCommit = true;
+
+ TreeWalk treeWalk = new TreeWalk(repo);
+ int dcIdx = treeWalk.addTree(new DirCacheIterator(index));
+ int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
+ int hIdx = -1;
+ if (headId != null)
+ hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId));
+ treeWalk.setRecursive(true);
+
+ while (treeWalk.next()) {
+ String path = treeWalk.getPathString();
+ // check if current entry's path matches a specified path
+ int pos = lookupOnly(path);
+
+ CanonicalTreeParser hTree = null;
+ if (hIdx != -1)
+ hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
+
+ if (pos >= 0) {
+ // include entry in commit
+
+ DirCacheIterator dcTree = treeWalk.getTree(dcIdx,
+ DirCacheIterator.class);
+ FileTreeIterator fTree = treeWalk.getTree(fIdx,
+ FileTreeIterator.class);
+
+ // check if entry refers to a tracked file
+ boolean tracked = dcTree != null || hTree != null;
+ if (!tracked)
+ break;
+
+ if (fTree != null) {
+ // create a new DirCacheEntry with data retrieved from disk
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);
+ long entryLength = fTree.getEntryLength();
+ dcEntry.setLength(entryLength);
+ dcEntry.setLastModified(fTree.getEntryLastModified());
+ dcEntry.setFileMode(fTree.getEntryFileMode());
+
+ boolean objectExists = (dcTree != null && fTree
+ .idEqual(dcTree))
+ || (hTree != null && fTree.idEqual(hTree));
+ if (objectExists) {
+ dcEntry.setObjectId(fTree.getEntryObjectId());
+ } else {
+ // insert object
+ if (inserter == null)
+ inserter = repo.newObjectInserter();
+
+ InputStream inputStream = fTree.openEntryStream();
+ try {
+ dcEntry.setObjectId(inserter.insert(
+ Constants.OBJ_BLOB, entryLength,
+ inputStream));
+ } finally {
+ inputStream.close();
+ }
+ }
+
+ // update index
+ dcEditor.add(new PathEdit(path) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.copyMetaData(dcEntry);
+ }
+ });
+ // add to temporary in-core index
+ dcBuilder.add(dcEntry);
+
+ if (emptyCommit && (hTree == null || !hTree.idEqual(fTree)))
+ // this is a change
+ emptyCommit = false;
+ } else {
+ // if no file exists on disk, remove entry from index and
+ // don't add it to temporary in-core index
+ dcEditor.add(new DeletePath(path));
+
+ if (emptyCommit && hTree != null)
+ // this is a change
+ emptyCommit = false;
+ }
+
+ // keep track of processed path
+ onlyProcessed[pos] = true;
+ } else {
+ // add entries from HEAD for all other paths
+ if (hTree != null) {
+ // create a new DirCacheEntry with data retrieved from HEAD
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);
+ dcEntry.setObjectId(hTree.getEntryObjectId());
+ dcEntry.setFileMode(hTree.getEntryFileMode());
+
+ // add to temporary in-core index
+ dcBuilder.add(dcEntry);
+ }
+ }
+ }
+
+ // there must be no unprocessed paths left at this point; otherwise an
+ // untracked or unknown path has been specified
+ for (int i = 0; i < onlyProcessed.length; i++)
+ if (!onlyProcessed[i])
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().entryNotFoundByPath, only.get(i)));
+
+ // there must be at least one change
+ if (emptyCommit)
+ throw new JGitInternalException(JGitText.get().emptyCommit);
+
+ // update index
+ dcEditor.commit();
+ // finish temporary in-core index used for this commit
+ dcBuilder.finish();
+ return inCoreIndex;
+ }
+
+ /**
+ * Look an entry's path up in the list of paths specified by the --only/ -o
+ * option
+ *
+ * In case the complete (file) path (e.g. "d1/d2/f1") cannot be found in
+ * <code>only</code>, lookup is also tried with (parent) directory paths
+ * (e.g. "d1/d2" and "d1").
+ *
+ * @param pathString
+ * entry's path
+ * @return the item's index in <code>only</code>; -1 if no item matches
+ */
+ private int lookupOnly(String pathString) {
+ int i = 0;
+ for (String o : only) {
+ String p = pathString;
+ while (true) {
+ if (p.equals(o))
+ return i;
+ int l = p.lastIndexOf("/");
+ if (l < 1)
+ break;
+ p = p.substring(0, l);
+ }
+ i++;
+ }
+ return -1;
+ }
+
/**
* Sets default values for not explicitly specified options. Then validates
* that all required data has been provided.
@@ -386,14 +563,20 @@ public class CommitCommand extends GitCommand<RevCommit> {
/**
* If set to true the Commit command automatically stages files that have
- * been modified and deleted, but new files you not known by the repository
- * are not affected. This corresponds to the parameter -a on the command
- * line.
+ * been modified and deleted, but new files not known by the repository are
+ * not affected. This corresponds to the parameter -a on the command line.
*
* @param all
* @return {@code this}
+ * @throws JGitInternalException
+ * in case of an illegal combination of arguments/ options
*/
public CommitCommand setAll(boolean all) {
+ checkCallable();
+ if (!only.isEmpty())
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().illegalCombinationOfArguments, "--all",
+ "--only"));
this.all = all;
return this;
}
@@ -407,8 +590,33 @@ public class CommitCommand extends GitCommand<RevCommit> {
* @return {@code this}
*/
public CommitCommand setAmend(boolean amend) {
+ checkCallable();
this.amend = amend;
return this;
}
+ /**
+ * Commit dedicated path only
+ *
+ * This method can be called several times to add multiple paths. Full file
+ * paths are supported as well as directory paths; in the latter case this
+ * commits all files/ directories below the specified path.
+ *
+ * @param only
+ * path to commit
+ * @return {@code this}
+ */
+ public CommitCommand setOnly(String only) {
+ checkCallable();
+ if (all)
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().illegalCombinationOfArguments, "--only",
+ "--all"));
+ String o = only.endsWith("/") ? only.substring(0, only.length() - 1)
+ : only;
+ // ignore duplicates
+ if (!this.only.contains(o))
+ this.only.add(o);
+ return this;
+ }
}