Merge changes Ib6689f54,I3b5c22ee * changes: Remove unused API problem filters Add missing @since tag for RevCommit#parents introduced in 61b4d105e4
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java index 5dfdfcf..57661a7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -15,13 +15,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; import java.util.Set; +import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.api.errors.FilterFailedException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoFilepatternException; @@ -34,6 +37,7 @@ import org.eclipse.jgit.lfs.BuiltinLFS; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; @@ -100,6 +104,43 @@ public void testAddExistingSingleFile() throws IOException, GitAPIException { } @Test + public void testAddLink() throws IOException, GitAPIException { + assumeTrue(db.getFS().supportsSymlinks()); + try (Git git = new Git(db)) { + writeTrashFile("a.txt", "a"); + File link = new File(db.getWorkTree(), "link"); + db.getFS().createSymLink(link, "a.txt"); + git.add().addFilepattern(".").call(); + assertEquals( + "[a.txt, mode:100644, content:a][link, mode:120000, content:a.txt]", + indexState(CONTENT)); + git.commit().setMessage("link").call(); + StoredConfig config = db.getConfig(); + config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_SYMLINKS, SymLinks.FALSE); + config.save(); + Files.delete(link.toPath()); + git.reset().setMode(ResetType.HARD).call(); + assertTrue(Files.isRegularFile(link.toPath())); + assertEquals( + "[a.txt, mode:100644, content:a][link, mode:120000, content:a.txt]", + indexState(CONTENT)); + writeTrashFile("link", "b.txt"); + git.add().addFilepattern("link").call(); + assertEquals( + "[a.txt, mode:100644, content:a][link, mode:120000, content:b.txt]", + indexState(CONTENT)); + config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_SYMLINKS, SymLinks.TRUE); + config.save(); + git.add().addFilepattern("link").call(); + assertEquals( + "[a.txt, mode:100644, content:a][link, mode:100644, content:b.txt]", + indexState(CONTENT)); + } + } + + @Test public void testCleanFilter() throws IOException, GitAPIException { writeTrashFile(".gitattributes", "*.txt filter=tstFilter"); writeTrashFile("src/a.tmp", "foo");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java index 0d49cd3..e463e90 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -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)); } }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 847ab0a..7319ff4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -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,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java index 1004d3e..17036a9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -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,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index f6fc393..2365c90 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -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();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index b108b0a..d8a61ec 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -1004,6 +1004,12 @@ public FileMode getIndexFileMode(DirCacheIterator indexIter) { return wtMode; } final FileMode iMode = indexIter.getEntryFileMode(); + if (iMode == FileMode.SYMLINK + && getOptions().getSymLinks() == SymLinks.FALSE + && (wtMode == FileMode.REGULAR_FILE + || wtMode == FileMode.EXECUTABLE_FILE)) { + return iMode; + } if (getOptions().isFileMode() && iMode != FileMode.GITLINK && iMode != FileMode.TREE) { return wtMode; }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/WorkTreeUpdater.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/WorkTreeUpdater.java index fb0b33a..88529ba 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/WorkTreeUpdater.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/WorkTreeUpdater.java
@@ -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); } } } @@ -554,22 +557,30 @@ public void updateFileWithContent( if (inCore) { return; } - CheckoutMetadata checkoutMetadata = new CheckoutMetadata(streamType, smudgeCommand); + CheckoutMetadata metadata = new CheckoutMetadata(streamType, + smudgeCommand); if (safeWrite) { - try (org.eclipse.jgit.util.TemporaryBuffer buffer = - new org.eclipse.jgit.util.TemporaryBuffer.LocalFile(null)) { - // Write to a buffer and copy to the file only if everything was fine. - DirCacheCheckout.getContent( - repo, path, checkoutMetadata, resultStreamLoader, null, buffer); - InputStream bufIn = buffer.openInputStream(); - Files.copy(bufIn, file.toPath(), StandardCopyOption.REPLACE_EXISTING); + // Write to a buffer and copy to the file only if everything was + // fine. + TemporaryBuffer buffer = new TemporaryBuffer.LocalFile(null); + try { + try (TemporaryBuffer buf = buffer) { + DirCacheCheckout.getContent(repo, path, metadata, + resultStreamLoader, workingTreeOptions, buf); + } + try (InputStream bufIn = buffer.openInputStream()) { + Files.copy(bufIn, file.toPath(), + StandardCopyOption.REPLACE_EXISTING); + } + } finally { + buffer.destroy(); } return; } - OutputStream outputStream = new FileOutputStream(file); - DirCacheCheckout.getContent( - repo, path, checkoutMetadata, resultStreamLoader, null, outputStream); - + try (OutputStream outputStream = new FileOutputStream(file)) { + DirCacheCheckout.getContent(repo, path, metadata, + resultStreamLoader, workingTreeOptions, outputStream); + } } /**