Progress reporting for checkout
The reason for the change is LFS: when using a lot of LFS files, checkout can take quite some time on larger repositories. To avoid "hanging" UI, provide progress reporting. Also implement (partial) progress reporting for cherry-pick, reset, revert which are using checkout internally. The feature is also useful without LFS, so it is independent of it. Change-Id: I021e764241f3c107eaf2771f6b5785245b146b42 Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
parent
14167272c2
commit
1c43af8b97
|
@ -57,6 +57,7 @@
|
|||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.TextProgressMonitor;
|
||||
import org.eclipse.jgit.pgm.internal.CLIText;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
@ -90,7 +91,8 @@ protected void run() throws Exception {
|
|||
}
|
||||
|
||||
try (Git git = new Git(db)) {
|
||||
CheckoutCommand command = git.checkout();
|
||||
CheckoutCommand command = git.checkout()
|
||||
.setProgressMonitor(new TextProgressMonitor(errw));
|
||||
if (paths.size() > 0) {
|
||||
command.setStartPoint(name);
|
||||
if (paths.size() == 1 && paths.get(0).equals(".")) { //$NON-NLS-1$
|
||||
|
|
|
@ -117,6 +117,7 @@ cantFindObjectInReversePackIndexForTheSpecifiedOffset=Can''t find object in (rev
|
|||
cantPassMeATree=Can't pass me a tree!
|
||||
channelMustBeInRange1_255=channel {0} must be in range [1, 255]
|
||||
characterClassIsNotSupported=The character class {0} is not supported.
|
||||
checkingOutFiles=Checking out files
|
||||
checkoutConflictWithFile=Checkout conflict with file: {0}
|
||||
checkoutConflictWithFiles=Checkout conflict with files: {0}
|
||||
checkoutUnexpectedResult=Checkout returned unexpected result {0}
|
||||
|
|
|
@ -76,8 +76,10 @@
|
|||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
|
||||
import org.eclipse.jgit.lib.FileMode;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.lib.RefUpdate.Result;
|
||||
|
@ -182,6 +184,8 @@ private Stage(int number) {
|
|||
|
||||
private Set<String> actuallyModifiedPaths;
|
||||
|
||||
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Constructor for CheckoutCommand
|
||||
*
|
||||
|
@ -266,6 +270,7 @@ public Ref call() throws GitAPIException, RefAlreadyExistsException,
|
|||
dco = new DirCacheCheckout(repo, headTree, dc,
|
||||
newCommit.getTree());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
try {
|
||||
dco.checkout();
|
||||
} catch (org.eclipse.jgit.errors.CheckoutConflictException e) {
|
||||
|
@ -346,6 +351,20 @@ private String getShortBranchName(Ref headRef) {
|
|||
return id.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param monitor
|
||||
* a progress monitor
|
||||
* @return this instance
|
||||
* @since 4.11
|
||||
*/
|
||||
public CheckoutCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||
if (monitor == null) {
|
||||
monitor = NullProgressMonitor.INSTANCE;
|
||||
}
|
||||
this.monitor = monitor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single slash-separated path to the list of paths to check out. To
|
||||
* check out all paths, use {@link #setAllPaths(boolean)}.
|
||||
|
|
|
@ -60,8 +60,10 @@
|
|||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Ref.Storage;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
@ -95,6 +97,8 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
|
|||
|
||||
private boolean noCommit = false;
|
||||
|
||||
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Constructor for CherryPickCommand
|
||||
*
|
||||
|
@ -160,6 +164,7 @@ public CherryPickResult call() throws GitAPIException, NoMessageException,
|
|||
newHead.getTree(), repo.lockDirCache(),
|
||||
merger.getResultTreeId());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
dco.checkout();
|
||||
if (!noCommit)
|
||||
newHead = new Git(getRepository()).commit()
|
||||
|
@ -332,6 +337,24 @@ public CherryPickCommand setNoCommit(boolean noCommit) {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The progress monitor associated with the cherry-pick operation. By
|
||||
* default, this is set to <code>NullProgressMonitor</code>
|
||||
*
|
||||
* @see NullProgressMonitor
|
||||
* @param monitor
|
||||
* a {@link org.eclipse.jgit.lib.ProgressMonitor}
|
||||
* @return {@code this}
|
||||
* @since 4.11
|
||||
*/
|
||||
public CherryPickCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||
if (monitor == null) {
|
||||
monitor = NullProgressMonitor.INSTANCE;
|
||||
}
|
||||
this.monitor = monitor;
|
||||
return this;
|
||||
}
|
||||
|
||||
private String calculateOurName(Ref headRef) {
|
||||
if (ourCommitName != null)
|
||||
return ourCommitName;
|
||||
|
|
|
@ -361,6 +361,7 @@ private void checkout(Repository clonedRepo, FetchResult result)
|
|||
DirCache dc = clonedRepo.lockDirCache();
|
||||
DirCacheCheckout co = new DirCacheCheckout(clonedRepo, dc,
|
||||
commit.getTree());
|
||||
co.setProgressMonitor(monitor);
|
||||
co.checkout();
|
||||
if (cloneSubmodules)
|
||||
cloneSubmodules(clonedRepo);
|
||||
|
|
|
@ -268,6 +268,7 @@ public MergeResult call() throws GitAPIException, NoHeadException,
|
|||
dco = new DirCacheCheckout(repo,
|
||||
repo.lockDirCache(), srcCommit.getTree());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
dco.checkout();
|
||||
RefUpdate refUpdate = repo
|
||||
.updateRef(head.getTarget().getName());
|
||||
|
@ -298,6 +299,7 @@ public MergeResult call() throws GitAPIException, NoHeadException,
|
|||
dco = new DirCacheCheckout(repo,
|
||||
headCommit.getTree(), repo.lockDirCache(),
|
||||
srcCommit.getTree());
|
||||
dco.setProgressMonitor(monitor);
|
||||
dco.setFailOnConflict(true);
|
||||
dco.checkout();
|
||||
String msg = null;
|
||||
|
@ -376,6 +378,7 @@ public MergeResult call() throws GitAPIException, NoHeadException,
|
|||
headCommit.getTree(), repo.lockDirCache(),
|
||||
merger.getResultTreeId());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
dco.checkout();
|
||||
|
||||
String msg = null;
|
||||
|
|
|
@ -932,6 +932,7 @@ private RevCommit checkoutCurrentHead() throws IOException, NoHeadException {
|
|||
try {
|
||||
DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree);
|
||||
dco.setFailOnConflict(false);
|
||||
dco.setProgressMonitor(monitor);
|
||||
boolean needsDeleteFiles = dco.checkout();
|
||||
if (needsDeleteFiles) {
|
||||
List<String> fileList = dco.getToBeDeleted();
|
||||
|
@ -1265,6 +1266,7 @@ private RevCommit tryFastForward(String headName, RevCommit oldCommit,
|
|||
|
||||
CheckoutCommand co = new CheckoutCommand(repo);
|
||||
try {
|
||||
co.setProgressMonitor(monitor);
|
||||
co.setName(newCommit.name()).call();
|
||||
if (headName.startsWith(Constants.R_HEADS)) {
|
||||
RefUpdate rup = repo.updateRef(headName);
|
||||
|
@ -1407,6 +1409,7 @@ private boolean checkoutCommit(String headName, RevCommit commit)
|
|||
DirCacheCheckout dco = new DirCacheCheckout(repo, head.getTree(),
|
||||
repo.lockDirCache(), commit.getTree());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
try {
|
||||
dco.checkout();
|
||||
} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
|
||||
|
|
|
@ -58,7 +58,9 @@
|
|||
import org.eclipse.jgit.dircache.DirCacheIterator;
|
||||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
@ -125,6 +127,8 @@ public enum ResetType {
|
|||
|
||||
private boolean isReflogDisabled;
|
||||
|
||||
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for ResetCommand.
|
||||
|
@ -336,6 +340,24 @@ private String getRefOrHEAD() {
|
|||
return Constants.HEAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* The progress monitor associated with the reset operation. By default,
|
||||
* this is set to <code>NullProgressMonitor</code>
|
||||
*
|
||||
* @see NullProgressMonitor
|
||||
* @param monitor
|
||||
* a {@link org.eclipse.jgit.lib.ProgressMonitor}
|
||||
* @return {@code this}
|
||||
* @since 4.11
|
||||
*/
|
||||
public ResetCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||
if (monitor == null) {
|
||||
monitor = NullProgressMonitor.INSTANCE;
|
||||
}
|
||||
this.monitor = monitor;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void resetIndexForPaths(ObjectId commitTree) {
|
||||
DirCache dc = null;
|
||||
try (final TreeWalk tw = new TreeWalk(repo)) {
|
||||
|
@ -420,6 +442,7 @@ private void checkoutIndex(ObjectId commitTree) throws IOException,
|
|||
DirCacheCheckout checkout = new DirCacheCheckout(repo, dc,
|
||||
commitTree);
|
||||
checkout.setFailOnConflict(false);
|
||||
checkout.setProgressMonitor(monitor);
|
||||
try {
|
||||
checkout.checkout();
|
||||
} catch (org.eclipse.jgit.errors.CheckoutConflictException cce) {
|
||||
|
|
|
@ -61,8 +61,10 @@
|
|||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Ref.Storage;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
@ -97,6 +99,8 @@ public class RevertCommand extends GitCommand<RevCommit> {
|
|||
|
||||
private MergeStrategy strategy = MergeStrategy.RECURSIVE;
|
||||
|
||||
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for RevertCommand.
|
||||
|
@ -178,6 +182,7 @@ public RevCommit call() throws NoMessageException, UnmergedPathsException,
|
|||
headCommit.getTree(), repo.lockDirCache(),
|
||||
merger.getResultTreeId());
|
||||
dco.setFailOnConflict(true);
|
||||
dco.setProgressMonitor(monitor);
|
||||
dco.checkout();
|
||||
try (Git git = new Git(getRepository())) {
|
||||
newHead = git.commit().setMessage(newMessage)
|
||||
|
@ -325,4 +330,22 @@ public RevertCommand setStrategy(MergeStrategy strategy) {
|
|||
this.strategy = strategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The progress monitor associated with the revert operation. By default,
|
||||
* this is set to <code>NullProgressMonitor</code>
|
||||
*
|
||||
* @see NullProgressMonitor
|
||||
* @param monitor
|
||||
* a {@link org.eclipse.jgit.lib.ProgressMonitor}
|
||||
* @return {@code this}
|
||||
* @since 4.11
|
||||
*/
|
||||
public RevertCommand setProgressMonitor(ProgressMonitor monitor) {
|
||||
if (monitor == null) {
|
||||
monitor = NullProgressMonitor.INSTANCE;
|
||||
}
|
||||
this.monitor = monitor;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -226,6 +226,7 @@ public Collection<String> call() throws InvalidConfigurationException,
|
|||
submoduleRepo, submoduleRepo.lockDirCache(),
|
||||
commit.getTree());
|
||||
co.setFailOnConflict(true);
|
||||
co.setProgressMonitor(monitor);
|
||||
co.checkout();
|
||||
RefUpdate refUpdate = submoduleRepo.updateRef(
|
||||
Constants.HEAD, true);
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jgit.api.errors.CanceledException;
|
||||
import org.eclipse.jgit.api.errors.FilterFailedException;
|
||||
import org.eclipse.jgit.attributes.FilterCommand;
|
||||
import org.eclipse.jgit.attributes.FilterCommandRegistry;
|
||||
|
@ -76,6 +77,7 @@
|
|||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
|
||||
|
@ -158,6 +160,8 @@ public CheckoutMetadata(EolStreamType eolStreamType,
|
|||
|
||||
private boolean performingCheckout;
|
||||
|
||||
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Get list of updated paths and smudgeFilterCommands
|
||||
*
|
||||
|
@ -287,6 +291,18 @@ public DirCacheCheckout(Repository repo, DirCache dc,
|
|||
this(repo, null, dc, mergeCommitTree, new FileTreeIterator(repo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a progress monitor which can be passed to built-in filter commands,
|
||||
* providing progress information for long running tasks.
|
||||
*
|
||||
* @param monitor
|
||||
* the {@link ProgressMonitor}
|
||||
* @since 4.11
|
||||
*/
|
||||
public void setProgressMonitor(ProgressMonitor monitor) {
|
||||
this.monitor = monitor != null ? monitor : NullProgressMonitor.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan head, index and merge tree. Used during normal checkout or merge
|
||||
* operations.
|
||||
|
@ -465,6 +481,10 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
|
|||
public boolean checkout() throws IOException {
|
||||
try {
|
||||
return doCheckout();
|
||||
} catch (CanceledException ce) {
|
||||
// should actually be propagated, but this would change a LOT of
|
||||
// APIs
|
||||
throw new IOException(ce);
|
||||
} finally {
|
||||
try {
|
||||
dc.unlock();
|
||||
|
@ -482,7 +502,7 @@ public boolean checkout() throws IOException {
|
|||
|
||||
private boolean doCheckout() throws CorruptObjectException, IOException,
|
||||
MissingObjectException, IncorrectObjectTypeException,
|
||||
CheckoutConflictException, IndexWriteException {
|
||||
CheckoutConflictException, IndexWriteException, CanceledException {
|
||||
toBeDeleted.clear();
|
||||
try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
|
||||
if (headCommitTree != null)
|
||||
|
@ -500,6 +520,10 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
|
|||
// update our index
|
||||
builder.finish();
|
||||
|
||||
// init progress reporting
|
||||
int numTotal = removed.size() + updated.size();
|
||||
monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
|
||||
|
||||
performingCheckout = true;
|
||||
File file = null;
|
||||
String last = null;
|
||||
|
@ -525,6 +549,12 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
|
|||
removeEmptyParents(new File(repo.getWorkTree(), last));
|
||||
last = r;
|
||||
}
|
||||
monitor.update(1);
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CanceledException(MessageFormat.format(
|
||||
JGitText.get().operationCanceled,
|
||||
JGitText.get().checkingOutFiles));
|
||||
}
|
||||
}
|
||||
if (file != null) {
|
||||
removeEmptyParents(file);
|
||||
|
@ -544,6 +574,13 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
|
|||
checkoutEntry(repo, entry, objectReader, false, meta);
|
||||
}
|
||||
e = null;
|
||||
|
||||
monitor.update(1);
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CanceledException(MessageFormat.format(
|
||||
JGitText.get().operationCanceled,
|
||||
JGitText.get().checkingOutFiles));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// We didn't actually modify the current entry nor any that
|
||||
|
@ -557,6 +594,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
|
|||
}
|
||||
throw ex;
|
||||
}
|
||||
monitor.endTask();
|
||||
|
||||
// commit the index builder - a new index is persisted
|
||||
if (!builder.commit())
|
||||
throw new IndexWriteException();
|
||||
|
|
|
@ -178,6 +178,7 @@ public static JGitText get() {
|
|||
/***/ public String cantPassMeATree;
|
||||
/***/ public String channelMustBeInRange1_255;
|
||||
/***/ public String characterClassIsNotSupported;
|
||||
/***/ public String checkingOutFiles;
|
||||
/***/ public String checkoutConflictWithFile;
|
||||
/***/ public String checkoutConflictWithFiles;
|
||||
/***/ public String checkoutUnexpectedResult;
|
||||
|
|
Loading…
Reference in New Issue