Browse Source

Add -o option to commit command

This change adds the --only/ -o option to the commit command.

Change-Id: I44352d56877f8204d985cb7a35a2e0faffb7d341
Signed-off-by: Philipp Thun <philipp.thun@sap.com>
stable-0.12
Philipp Thun 14 years ago
parent
commit
a490afedba
  1. 6
      org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties
  2. 1
      org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java
  3. 19
      org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
  4. 1245
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java
  5. 2
      org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
  6. 2
      org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
  7. 214
      org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java

6
org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/CLIText.properties

@ -62,8 +62,9 @@ metaVar_base=base
metaVar_bucket=BUCKET metaVar_bucket=BUCKET
metaVar_command=command metaVar_command=command
metaVar_commandDetail=DETAIL metaVar_commandDetail=DETAIL
metaVar_commitOrTag=COMMIT|TAG
metaVar_commitish=commit-ish metaVar_commitish=commit-ish
metaVar_commitOrTag=COMMIT|TAG
metaVar_commitPaths=paths
metaVar_configFile=FILE metaVar_configFile=FILE
metaVar_connProp=conn.prop metaVar_connProp=conn.prop
metaVar_diffAlg=ALGORITHM metaVar_diffAlg=ALGORITHM
@ -113,6 +114,7 @@ notFound=!! NOT FOUND !!
noteObjectTooLargeToPrint=Note object {0} too large to print noteObjectTooLargeToPrint=Note object {0} too large to print
onlyOneMetaVarExpectedIn=Only one {0} expected in {1}. onlyOneMetaVarExpectedIn=Only one {0} expected in {1}.
pushTo=To {0} pushTo=To {0}
pathsRequired=at least one path has to be specified when using --only
remoteMessage=remote: {0} remoteMessage=remote: {0}
remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0} remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0}
remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs remoteSideDoesNotSupportDeletingRefs=remote side does not support deleting refs
@ -125,6 +127,8 @@ unsupportedOperation=Unsupported operation: {0}
usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service
usage_CommitAuthor=Override the author name used in the commit. You can use the standard A U Thor <author@example.com> format. usage_CommitAuthor=Override the author name used in the commit. You can use the standard A U Thor <author@example.com> format.
usage_CommitMessage=Use the given <msg> as the commit message usage_CommitMessage=Use the given <msg> as the commit message
usage_CommitOnly=commit specified paths only
usage_CommitPaths=see --only
usage_CreateABareRepository=Create a bare repository usage_CreateABareRepository=Create a bare repository
usage_CreateATag=Create a tag usage_CreateATag=Create a tag
usage_CreateAnEmptyGitRepository=Create an empty git repository usage_CreateAnEmptyGitRepository=Create an empty git repository

1
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CLIText.java

@ -133,6 +133,7 @@ public class CLIText extends TranslationBundle {
/***/ public String noteObjectTooLargeToPrint; /***/ public String noteObjectTooLargeToPrint;
/***/ public String onlyOneMetaVarExpectedIn; /***/ public String onlyOneMetaVarExpectedIn;
/***/ public String pushTo; /***/ public String pushTo;
/***/ public String pathsRequired;
/***/ public String remoteMessage; /***/ public String remoteMessage;
/***/ public String remoteRefObjectChangedIsNotExpectedOne; /***/ public String remoteRefObjectChangedIsNotExpectedOne;
/***/ public String remoteSideDoesNotSupportDeletingRefs; /***/ public String remoteSideDoesNotSupportDeletingRefs;

19
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java

@ -37,6 +37,9 @@
*/ */
package org.eclipse.jgit.pgm; package org.eclipse.jgit.pgm;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
@ -47,6 +50,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.RawParseUtils;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option; import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_recordChangesToRepository") @Command(common = true, usage = "usage_recordChangesToRepository")
@ -54,12 +58,18 @@ class Commit extends TextBuiltin {
// I don't support setting the committer, because also the native git // I don't support setting the committer, because also the native git
// command doesn't allow this. // command doesn't allow this.
@Option(name = "--author", metaVar="metaVar_author", usage = "usage_CommitAuthor") @Option(name = "--author", metaVar = "metaVar_author", usage = "usage_CommitAuthor")
private String author; private String author;
@Option(name = "--message", aliases = { "-m" }, metaVar="metaVar_message", usage="usage_CommitMessage", required=true) @Option(name = "--message", aliases = { "-m" }, metaVar = "metaVar_message", usage = "usage_CommitMessage", required = true)
private String message; private String message;
@Option(name = "--only", aliases = { "-o" }, usage = "usage_CommitOnly")
private boolean only;
@Argument(metaVar = "metaVar_commitPaths", usage = "usage_CommitPaths")
private List<String> paths = new ArrayList<String>();
@Override @Override
protected void run() throws NoHeadException, NoMessageException, protected void run() throws NoHeadException, NoMessageException,
ConcurrentRefUpdateException, JGitInternalException, Exception { ConcurrentRefUpdateException, JGitInternalException, Exception {
@ -68,6 +78,11 @@ class Commit extends TextBuiltin {
commitCmd.setAuthor(RawParseUtils.parsePersonIdent(author)); commitCmd.setAuthor(RawParseUtils.parsePersonIdent(author));
if (message != null) if (message != null)
commitCmd.setMessage(message); commitCmd.setMessage(message);
if (only && paths.isEmpty())
throw die(CLIText.get().pathsRequired);
if (!paths.isEmpty())
for (String p : paths)
commitCmd.setOnly(p);
Ref head = db.getRef(Constants.HEAD); Ref head = db.getRef(Constants.HEAD);
RevCommit commit = commitCmd.call(); RevCommit commit = commitCmd.call();

1245
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTests.java

File diff suppressed because it is too large Load Diff

2
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} duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
duplicateStagesNotAllowed=Duplicate stages not allowed duplicateStagesNotAllowed=Duplicate stages not allowed
eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called. eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
emptyCommit=No changes
emptyPathNotPermitted=Empty path not permitted. emptyPathNotPermitted=Empty path not permitted.
encryptionError=Encryption error: {0} encryptionError=Encryption error: {0}
endOfFileInEscape=End of file in escape endOfFileInEscape=End of file in escape
@ -207,6 +208,7 @@ hunkBelongsToAnotherFile=Hunk belongs to another file
hunkDisconnectedFromFile=Hunk disconnected from file hunkDisconnectedFromFile=Hunk disconnected from file
hunkHeaderDoesNotMatchBodyLineCountOf=Hunk header {0} does not match body line count of {1} hunkHeaderDoesNotMatchBodyLineCountOf=Hunk header {0} does not match body line count of {1}
illegalArgumentNotA=Not {0} illegalArgumentNotA=Not {0}
illegalCombinationOfArguments=The combination of arguments {0} and {1} is not allowed
illegalStateExists=exists {0} illegalStateExists=exists {0}
improperlyPaddedBase64Input=Improperly padded Base64 input. improperlyPaddedBase64Input=Improperly padded Base64 input.
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded inMemoryBufferLimitExceeded=In-memory buffer limit exceeded

2
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java

@ -213,6 +213,7 @@ public class JGitText extends TranslationBundle {
/***/ public String duplicateRemoteRefUpdateIsIllegal; /***/ public String duplicateRemoteRefUpdateIsIllegal;
/***/ public String duplicateStagesNotAllowed; /***/ public String duplicateStagesNotAllowed;
/***/ public String eitherGitDirOrWorkTreeRequired; /***/ public String eitherGitDirOrWorkTreeRequired;
/***/ public String emptyCommit;
/***/ public String emptyPathNotPermitted; /***/ public String emptyPathNotPermitted;
/***/ public String encryptionError; /***/ public String encryptionError;
/***/ public String endOfFileInEscape; /***/ public String endOfFileInEscape;
@ -267,6 +268,7 @@ public class JGitText extends TranslationBundle {
/***/ public String hunkDisconnectedFromFile; /***/ public String hunkDisconnectedFromFile;
/***/ public String hunkHeaderDoesNotMatchBodyLineCountOf; /***/ public String hunkHeaderDoesNotMatchBodyLineCountOf;
/***/ public String illegalArgumentNotA; /***/ public String illegalArgumentNotA;
/***/ public String illegalCombinationOfArguments;
/***/ public String illegalStateExists; /***/ public String illegalStateExists;
/***/ public String improperlyPaddedBase64Input; /***/ public String improperlyPaddedBase64Input;
/***/ public String inMemoryBufferLimitExceeded; /***/ public String inMemoryBufferLimitExceeded;

214
org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java

@ -43,7 +43,9 @@
package org.eclipse.jgit.api; package org.eclipse.jgit.api;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; 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.NoMessageException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCache; 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.errors.UnmergedPathException;
import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants; 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.lib.RepositoryState;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk; 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 * 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 boolean all;
private List<String> only = new ArrayList<String>();
private boolean[] onlyProcessed;
private boolean amend; private boolean amend;
/** /**
@ -170,6 +185,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
// lock the index // lock the index
DirCache index = repo.lockDirCache(); DirCache index = repo.lockDirCache();
try { try {
if (!only.isEmpty())
index = createTemporaryIndex(headId, index);
ObjectInserter odi = repo.newObjectInserter(); ObjectInserter odi = repo.newObjectInserter();
try { try {
// Write the index as tree to the object database. This may // 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 * Sets default values for not explicitly specified options. Then validates
* that all required data has been provided. * 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 * 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 * been modified and deleted, but new files not known by the repository are
* are not affected. This corresponds to the parameter -a on the command * not affected. This corresponds to the parameter -a on the command line.
* line.
* *
* @param all * @param all
* @return {@code this} * @return {@code this}
* @throws JGitInternalException
* in case of an illegal combination of arguments/ options
*/ */
public CommitCommand setAll(boolean all) { public CommitCommand setAll(boolean all) {
checkCallable();
if (!only.isEmpty())
throw new JGitInternalException(MessageFormat.format(
JGitText.get().illegalCombinationOfArguments, "--all",
"--only"));
this.all = all; this.all = all;
return this; return this;
} }
@ -407,8 +590,33 @@ public class CommitCommand extends GitCommand<RevCommit> {
* @return {@code this} * @return {@code this}
*/ */
public CommitCommand setAmend(boolean amend) { public CommitCommand setAmend(boolean amend) {
checkCallable();
this.amend = amend; this.amend = amend;
return this; 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;
}
} }

Loading…
Cancel
Save