DirCacheCheckout: load WorkingTreeOptions only once

Previous code loaded the WorkingTreeOptions afresh for every single
file being checked out. This checked the git config (all three files,
repo, user and system config) for having been modified every time.

These checks can be costly, for instance on Windows, or if one of the
three config files is not on a local disk, or on an otherwise slow
storage.

Improve this by loading the options and thus checking the git config
only once before the checkout.

Bug: 579715
Change-Id: I21cd5a808f9d90b5ca2d022f91f0eeb8ca26091c
Signed-off-by: Thomas Wolf <twolf@apache.org>
This commit is contained in:
Thomas Wolf 2022-08-14 16:34:50 +02:00 committed by Matthias Sohn
parent 134ee334fb
commit b255eb0fb6
5 changed files with 103 additions and 43 deletions

View File

@ -303,12 +303,16 @@ public void testIsModifiedSymlinkAsFile() throws Exception {
DirCacheEntry dce = db.readDirCache().getEntry("symlink");
dce.setFileMode(FileMode.SYMLINK);
try (ObjectReader objectReader = db.newObjectReader()) {
DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null);
WorkingTreeOptions options = db.getConfig()
.get(WorkingTreeOptions.KEY);
DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null,
options);
FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
db.getConfig().get(WorkingTreeOptions.KEY));
while (!fti.getEntryPathString().equals("symlink"))
options);
while (!fti.getEntryPathString().equals("symlink")) {
fti.next(1);
}
assertFalse(fti.isModified(dce, false, objectReader));
}
}

View File

@ -55,6 +55,7 @@
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
/**
@ -411,6 +412,8 @@ public CheckoutCommand setAllPaths(boolean all) {
protected CheckoutCommand checkoutPaths() throws IOException,
RefNotFoundException {
actuallyModifiedPaths = new HashSet<>();
WorkingTreeOptions options = repo.getConfig()
.get(WorkingTreeOptions.KEY);
DirCache dc = repo.lockDirCache();
try (RevWalk revWalk = new RevWalk(repo);
TreeWalk treeWalk = new TreeWalk(repo,
@ -419,10 +422,10 @@ protected CheckoutCommand checkoutPaths() throws IOException,
if (!checkoutAllPaths)
treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
if (isCheckoutIndex())
checkoutPathsFromIndex(treeWalk, dc);
checkoutPathsFromIndex(treeWalk, dc, options);
else {
RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
checkoutPathsFromCommit(treeWalk, dc, commit);
checkoutPathsFromCommit(treeWalk, dc, commit, options);
}
} finally {
try {
@ -439,7 +442,8 @@ protected CheckoutCommand checkoutPaths() throws IOException,
return this;
}
private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc)
private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc,
WorkingTreeOptions options)
throws IOException {
DirCacheIterator dci = new DirCacheIterator(dc);
treeWalk.addTree(dci);
@ -465,8 +469,9 @@ public void apply(DirCacheEntry ent) {
if (stage > DirCacheEntry.STAGE_0) {
if (checkoutStage != null) {
if (stage == checkoutStage.number) {
checkoutPath(ent, r, new CheckoutMetadata(
eolStreamType, filterCommand));
checkoutPath(ent, r, options,
new CheckoutMetadata(eolStreamType,
filterCommand));
actuallyModifiedPaths.add(path);
}
} else {
@ -475,8 +480,9 @@ public void apply(DirCacheEntry ent) {
throw new JGitInternalException(e.getMessage(), e);
}
} else {
checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
filterCommand));
checkoutPath(ent, r, options,
new CheckoutMetadata(eolStreamType,
filterCommand));
actuallyModifiedPaths.add(path);
}
}
@ -488,7 +494,7 @@ public void apply(DirCacheEntry ent) {
}
private void checkoutPathsFromCommit(TreeWalk treeWalk, DirCache dc,
RevCommit commit) throws IOException {
RevCommit commit, WorkingTreeOptions options) throws IOException {
treeWalk.addTree(commit.getTree());
final ObjectReader r = treeWalk.getObjectReader();
DirCacheEditor editor = dc.editor();
@ -510,7 +516,7 @@ public void apply(DirCacheEntry ent) {
}
ent.setObjectId(blobId);
ent.setFileMode(mode);
checkoutPath(ent, r,
checkoutPath(ent, r, options,
new CheckoutMetadata(eolStreamType, filterCommand));
actuallyModifiedPaths.add(path);
}
@ -520,10 +526,10 @@ public void apply(DirCacheEntry ent) {
}
private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
CheckoutMetadata checkoutMetadata) {
WorkingTreeOptions options, CheckoutMetadata checkoutMetadata) {
try {
DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
checkoutMetadata);
checkoutMetadata, options);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile,

View File

@ -48,6 +48,7 @@
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
/**
* Command class to apply a stashed commit.
@ -382,6 +383,8 @@ private void resetIndex(RevTree tree) throws IOException {
private void resetUntracked(RevTree tree) throws CheckoutConflictException,
IOException {
Set<String> actuallyModifiedPaths = new HashSet<>();
WorkingTreeOptions options = repo.getConfig()
.get(WorkingTreeOptions.KEY);
// TODO maybe NameConflictTreeWalk ?
try (TreeWalk walk = new TreeWalk(repo)) {
walk.addTree(tree);
@ -413,7 +416,7 @@ private void resetUntracked(RevTree tree) throws CheckoutConflictException,
}
}
checkoutPath(entry, reader,
checkoutPath(entry, reader, options,
new CheckoutMetadata(eolStreamType, null));
actuallyModifiedPaths.add(entry.getPathString());
}
@ -426,10 +429,10 @@ private void resetUntracked(RevTree tree) throws CheckoutConflictException,
}
private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
CheckoutMetadata checkoutMetadata) {
WorkingTreeOptions options, CheckoutMetadata checkoutMetadata) {
try {
DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
checkoutMetadata);
checkoutMetadata, options);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile,

View File

@ -143,6 +143,8 @@ public CheckoutMetadata(EolStreamType eolStreamType,
private boolean performingCheckout;
private WorkingTreeOptions options;
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
/**
@ -362,9 +364,12 @@ private int addTree(TreeWalk tw, ObjectId id) throws MissingObjectException,
* Processing an entry in the context of {@link #prescanOneTree()} when only
* one tree is given
*
* @param m the tree to merge
* @param i the index
* @param f the working tree
* @param m
* the tree to merge
* @param i
* the index
* @param f
* the working tree
* @throws IOException
*/
void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
@ -489,6 +494,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
MissingObjectException, IncorrectObjectTypeException,
CheckoutConflictException, IndexWriteException, CanceledException {
toBeDeleted.clear();
options = repo.getConfig()
.get(WorkingTreeOptions.KEY);
try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
if (headCommitTree != null)
preScanTwoTrees();
@ -558,7 +565,8 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
if (FileMode.GITLINK.equals(entry.getRawMode())) {
checkoutGitlink(path, entry);
} else {
checkoutEntry(repo, entry, objectReader, false, meta);
checkoutEntry(repo, entry, objectReader, false, meta,
options);
}
e = null;
@ -594,7 +602,7 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
}
if (entry.getStage() == DirCacheEntry.STAGE_3) {
checkoutEntry(repo, entry, objectReader, false,
null);
null, options);
break;
}
++entryIdx;
@ -1226,7 +1234,7 @@ private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
checkoutEntry(repo, e, walk.getObjectReader(), false,
new CheckoutMetadata(walk.getEolStreamType(CHECKOUT_OP),
walk.getFilterCommand(
Constants.ATTR_FILTER_TYPE_SMUDGE)));
Constants.ATTR_FILTER_TYPE_SMUDGE)), options);
}
}
}
@ -1392,12 +1400,6 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
* cannot be renamed to file or link without deleting it recursively.
* </p>
*
* <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* </p>
*
* @param repo
* repository managing the destination work tree.
* @param entry
@ -1407,15 +1409,16 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
* @throws java.io.IOException
* @since 3.6
* @deprecated since 5.1, use
* {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata)}
* {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
* instead
*/
@Deprecated
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or) throws IOException {
checkoutEntry(repo, entry, or, false, null);
checkoutEntry(repo, entry, or, false, null, null);
}
/**
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
@ -1428,12 +1431,6 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
* recursively, independently if has any content.
* </p>
*
* <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* </p>
*
* @param repo
* repository managing the destination work tree.
* @param entry
@ -1452,12 +1449,58 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
* </ul>
* @throws java.io.IOException
* @since 4.2
* @deprecated since 6.3, use
* {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
* instead
*/
@Deprecated
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or, boolean deleteRecursive,
CheckoutMetadata checkoutMetadata) throws IOException {
if (checkoutMetadata == null)
checkoutEntry(repo, entry, or, deleteRecursive, checkoutMetadata, null);
}
/**
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the
* final filename.
*
* <p>
* <b>Note:</b> if the entry path on local file system exists as a file, it
* will be deleted and if it exists as a directory, it will be deleted
* recursively, independently if has any content.
* </p>
*
* @param repo
* repository managing the destination work tree.
* @param entry
* the entry containing new mode and content
* @param or
* object reader to use for checkout
* @param deleteRecursive
* true to recursively delete final path if it exists on the file
* system
* @param checkoutMetadata
* containing
* <ul>
* <li>smudgeFilterCommand to be run for smudging the entry to be
* checked out</li>
* <li>eolStreamType used for stream conversion</li>
* </ul>
* @param options
* {@link WorkingTreeOptions} that are effective; if {@code null}
* they are loaded from the repository config
* @throws java.io.IOException
* @since 6.3
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or, boolean deleteRecursive,
CheckoutMetadata checkoutMetadata, WorkingTreeOptions options)
throws IOException {
if (checkoutMetadata == null) {
checkoutMetadata = CheckoutMetadata.EMPTY;
}
ObjectLoader ol = or.open(entry.getObjectId());
File f = new File(repo.getWorkTree(), entry.getPathString());
File parentDir = f.getParentFile();
@ -1466,7 +1509,8 @@ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
}
FileUtils.mkdirs(parentDir, true);
FS fs = repo.getFS();
WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
WorkingTreeOptions opt = options != null ? options
: repo.getConfig().get(WorkingTreeOptions.KEY);
if (entry.getFileMode() == FileMode.SYMLINK
&& opt.getSymLinks() == SymLinks.TRUE) {
byte[] bytes = ol.getBytes();

View File

@ -495,7 +495,9 @@ private void checkout() throws NoWorkTreeException, IOException {
new File(nonNullNonBareRepo().getWorkTree(), entry.getKey()).mkdirs();
} else {
DirCacheCheckout.checkoutEntry(
repo, dirCacheEntry, reader, false, checkoutMetadata.get(entry.getKey()));
repo, dirCacheEntry, reader, false,
checkoutMetadata.get(entry.getKey()),
workingTreeOptions);
result.modifiedFiles.add(entry.getKey());
}
}
@ -520,7 +522,8 @@ public void revertModifiedFiles() throws IOException {
DirCacheEntry entry = dirCache.getEntry(path);
if (entry != null) {
DirCacheCheckout.checkoutEntry(
repo, entry, reader, false, cleanupMetadata.get(path));
repo, entry, reader, false, cleanupMetadata.get(path),
workingTreeOptions);
}
}
}
@ -563,7 +566,7 @@ public void updateFileWithContent(
try {
try (TemporaryBuffer buf = buffer) {
DirCacheCheckout.getContent(repo, path, metadata,
resultStreamLoader, null, buf);
resultStreamLoader, workingTreeOptions, buf);
}
try (InputStream bufIn = buffer.openInputStream()) {
Files.copy(bufIn, file.toPath(),
@ -576,7 +579,7 @@ public void updateFileWithContent(
}
try (OutputStream outputStream = new FileOutputStream(file)) {
DirCacheCheckout.getContent(repo, path, metadata,
resultStreamLoader, null, outputStream);
resultStreamLoader, workingTreeOptions, outputStream);
}
}