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>
This commit is contained in:
parent
42f0b11153
commit
a490afedba
|
@ -62,8 +62,9 @@ metaVar_base=base
|
|||
metaVar_bucket=BUCKET
|
||||
metaVar_command=command
|
||||
metaVar_commandDetail=DETAIL
|
||||
metaVar_commitOrTag=COMMIT|TAG
|
||||
metaVar_commitish=commit-ish
|
||||
metaVar_commitOrTag=COMMIT|TAG
|
||||
metaVar_commitPaths=paths
|
||||
metaVar_configFile=FILE
|
||||
metaVar_connProp=conn.prop
|
||||
metaVar_diffAlg=ALGORITHM
|
||||
|
@ -113,6 +114,7 @@ notFound=!! NOT FOUND !!
|
|||
noteObjectTooLargeToPrint=Note object {0} too large to print
|
||||
onlyOneMetaVarExpectedIn=Only one {0} expected in {1}.
|
||||
pushTo=To {0}
|
||||
pathsRequired=at least one path has to be specified when using --only
|
||||
remoteMessage=remote: {0}
|
||||
remoteRefObjectChangedIsNotExpectedOne=remote ref object changed - is not expected one {0}
|
||||
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_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_CommitOnly=commit specified paths only
|
||||
usage_CommitPaths=see --only
|
||||
usage_CreateABareRepository=Create a bare repository
|
||||
usage_CreateATag=Create a tag
|
||||
usage_CreateAnEmptyGitRepository=Create an empty git repository
|
||||
|
|
|
@ -133,6 +133,7 @@ public static CLIText get() {
|
|||
/***/ public String noteObjectTooLargeToPrint;
|
||||
/***/ public String onlyOneMetaVarExpectedIn;
|
||||
/***/ public String pushTo;
|
||||
/***/ public String pathsRequired;
|
||||
/***/ public String remoteMessage;
|
||||
/***/ public String remoteRefObjectChangedIsNotExpectedOne;
|
||||
/***/ public String remoteSideDoesNotSupportDeletingRefs;
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
*/
|
||||
package org.eclipse.jgit.pgm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jgit.api.CommitCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
|
||||
|
@ -47,6 +50,7 @@
|
|||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.util.RawParseUtils;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
@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
|
||||
// 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;
|
||||
|
||||
@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;
|
||||
|
||||
@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
|
||||
protected void run() throws NoHeadException, NoMessageException,
|
||||
ConcurrentRefUpdateException, JGitInternalException, Exception {
|
||||
|
@ -68,6 +78,11 @@ protected void run() throws NoHeadException, NoMessageException,
|
|||
commitCmd.setAuthor(RawParseUtils.parsePersonIdent(author));
|
||||
if (message != null)
|
||||
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);
|
||||
RevCommit commit = commitCmd.call();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -213,6 +213,7 @@ public static JGitText get() {
|
|||
/***/ 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 static JGitText get() {
|
|||
/***/ public String hunkDisconnectedFromFile;
|
||||
/***/ public String hunkHeaderDoesNotMatchBodyLineCountOf;
|
||||
/***/ public String illegalArgumentNotA;
|
||||
/***/ public String illegalCombinationOfArguments;
|
||||
/***/ public String illegalStateExists;
|
||||
/***/ public String improperlyPaddedBase64Input;
|
||||
/***/ public String inMemoryBufferLimitExceeded;
|
||||
|
|
|
@ -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.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.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 RevCommit call() throws NoHeadException, NoMessageException,
|
|||
// 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 RevCommit call() throws NoHeadException, NoMessageException,
|
|||
}
|
||||
}
|
||||
|
||||
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 PersonIdent getAuthor() {
|
|||
|
||||
/**
|
||||
* 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 CommitCommand setAll(boolean all) {
|
|||
* @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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue