From 3758ba50adefbd71f8adc27961ac90b0fe2d6f2a Mon Sep 17 00:00:00 2001 From: Vladimir Kotal Date: Wed, 1 Mar 2017 17:08:43 +0100 Subject: [PATCH 1/4] use --follow without -r for getting history of renamed files in Mercurial fixes #970 --- .../nbproject/project.properties | 2 +- .../opengrok/history/FileHistoryCache.java | 140 +++++++------- .../opengrok/history/MercurialRepository.java | 71 ++++--- .../history/FileHistoryCacheTest.java | 177 ++++++++++++++++-- .../history/MercurialRepositoryTest.java | 9 +- .../history/hg-export-renamed-again.txt | 32 ++++ .../history/hg-export-renamed-branched.txt | 22 +++ 7 files changed, 345 insertions(+), 108 deletions(-) create mode 100644 test/org/opensolaris/opengrok/history/hg-export-renamed-again.txt create mode 100644 test/org/opensolaris/opengrok/history/hg-export-renamed-branched.txt diff --git a/opengrok-web-nbproject/nbproject/project.properties b/opengrok-web-nbproject/nbproject/project.properties index b32b18fbf46..b86e1658e5d 100644 --- a/opengrok-web-nbproject/nbproject/project.properties +++ b/opengrok-web-nbproject/nbproject/project.properties @@ -34,7 +34,7 @@ endorsed.classpath= excludes= file.reference.bcel-6.0.jar=../lib/bcel-6.0.jar file.reference.json-simple-1.1.1.jar=../lib/json-simple-1.1.1.jar -j2ee.platform.classpath=${j2ee.server.home}/lib/annotations-api.jar:${j2ee.server.home}/lib/catalina-ant.jar:${j2ee.server.home}/lib/catalina-ha.jar:${j2ee.server.home}/lib/catalina-storeconfig.jar:${j2ee.server.home}/lib/catalina-tribes.jar:${j2ee.server.home}/lib/catalina.jar:${j2ee.server.home}/lib/ecj-4.4.2.jar:${j2ee.server.home}/lib/el-api.jar:${j2ee.server.home}/lib/jasper-el.jar:${j2ee.server.home}/lib/jasper.jar:${j2ee.server.home}/lib/jsp-api.jar:${j2ee.server.home}/lib/servlet-api.jar:${j2ee.server.home}/lib/tomcat-api.jar:${j2ee.server.home}/lib/tomcat-coyote.jar:${j2ee.server.home}/lib/tomcat-dbcp.jar:${j2ee.server.home}/lib/tomcat-i18n-es.jar:${j2ee.server.home}/lib/tomcat-i18n-fr.jar:${j2ee.server.home}/lib/tomcat-i18n-ja.jar:${j2ee.server.home}/lib/tomcat-jdbc.jar:${j2ee.server.home}/lib/tomcat-jni.jar:${j2ee.server.home}/lib/tomcat-util-scan.jar:${j2ee.server.home}/lib/tomcat-util.jar:${j2ee.server.home}/lib/tomcat-websocket.jar:${j2ee.server.home}/lib/websocket-api.jar +j2ee.platform.classpath=${j2ee.server.home}/lib/annotations-api.jar:${j2ee.server.home}/lib/catalina-ant.jar:${j2ee.server.home}/lib/catalina-ha.jar:${j2ee.server.home}/lib/catalina-storeconfig.jar:${j2ee.server.home}/lib/catalina-tribes.jar:${j2ee.server.home}/lib/catalina.jar:${j2ee.server.home}/lib/ecj-4.5.jar:${j2ee.server.home}/lib/el-api.jar:${j2ee.server.home}/lib/jasper-el.jar:${j2ee.server.home}/lib/jasper.jar:${j2ee.server.home}/lib/jsp-api.jar:${j2ee.server.home}/lib/servlet-api.jar:${j2ee.server.home}/lib/tomcat-api.jar:${j2ee.server.home}/lib/tomcat-coyote.jar:${j2ee.server.home}/lib/tomcat-dbcp.jar:${j2ee.server.home}/lib/tomcat-i18n-es.jar:${j2ee.server.home}/lib/tomcat-i18n-fr.jar:${j2ee.server.home}/lib/tomcat-i18n-ja.jar:${j2ee.server.home}/lib/tomcat-jdbc.jar:${j2ee.server.home}/lib/tomcat-jni.jar:${j2ee.server.home}/lib/tomcat-util-scan.jar:${j2ee.server.home}/lib/tomcat-util.jar:${j2ee.server.home}/lib/tomcat-websocket.jar:${j2ee.server.home}/lib/websocket-api.jar lucene.version=6.4.1 lucene-core.jar=lucene-core-${lucene.version}.jar lucene-analyzers-common.jar=lucene-analyzers-common-${lucene.version}.jar diff --git a/src/org/opensolaris/opengrok/history/FileHistoryCache.java b/src/org/opensolaris/opengrok/history/FileHistoryCache.java index fb0ffa1541e..637fcd23b7a 100644 --- a/src/org/opensolaris/opengrok/history/FileHistoryCache.java +++ b/src/org/opensolaris/opengrok/history/FileHistoryCache.java @@ -80,27 +80,29 @@ public boolean isHistoryIndexDone() { /** * Generate history for single file. - * @param map_entry entry mapping filename to list of history entries + * @param filename name of the file + * @param historyEntries list of HistoryEntry objects forming the (incremental) history of the file * @param env runtime environment * @param repository repository object in which the file belongs - * @param test file object + * @param srcFile file object * @param root root of the source repository - * @param renamed true if the files was renamed in the past + * @param renamed true if the file was renamed in the past */ - private void doFileHistory(Map.Entry> map_entry, + private void doFileHistory(String filename, List historyEntries, RuntimeEnvironment env, Repository repository, - File test, File root, boolean renamed) throws HistoryException { + File srcFile, File root, boolean renamed) throws HistoryException { History hist = null; /* - * Certain files require special handling - this is mainly for - * files which have been renamed in Mercurial repository. - * This ensures that their complete history (follow) will be - * saved. + * If the file was renamed (in the changesets that are being indexed), + * its history is not stored in the historyEntries so it needs to be acquired + * directly from the repository. + * This ensures that complete history of the file (across renames) + * will be saved. */ if (renamed) { - hist = repository.getHistory(test); + hist = repository.getHistory(srcFile); } if (hist == null) { @@ -108,12 +110,12 @@ private void doFileHistory(Map.Entry> map_entry, // File based history cache does not store files for individual // changesets so strip them. - for (HistoryEntry ent : map_entry.getValue()) { + for (HistoryEntry ent : historyEntries) { ent.strip(); } // add all history entries - hist.setHistoryEntries(map_entry.getValue()); + hist.setHistoryEntries(historyEntries); } else { for (HistoryEntry ent : hist.getHistoryEntries()) { ent.strip(); @@ -125,20 +127,19 @@ private void doFileHistory(Map.Entry> map_entry, repository.assignTagsInHistory(hist); } - File file = new File(root, map_entry.getKey()); + File file = new File(root, filename); if (!file.isDirectory()) { storeFile(hist, file, repository); } } - private boolean isRenamedFile(Map.Entry> map_entry, RuntimeEnvironment env, + private boolean isRenamedFile(String filename, + RuntimeEnvironment env, Repository repository, History history) throws IOException { - String fullfile = map_entry.getKey(); String repodir = env.getPathRelativeToSourceRoot( new File(repository.getDirectoryName()), 0); - String shortestfile = fullfile.substring(repodir.length() + 1); + String shortestfile = filename.substring(repodir.length() + 1); return (history.isRenamed(shortestfile)); } @@ -211,6 +212,55 @@ private static History readCache(File file) throws IOException { } } + /** + * Store history in file on disk. + * @param dir directory where the file will be saved + * @param history history to store + * @param cacheFile the file to store the history to + * @throws HistoryException + */ + private void writeHistoryToFile(File dir, History history, File cacheFile) throws HistoryException { + // We have a problem that multiple threads may access the cache layer + // at the same time. Since I would like to avoid read-locking, I just + // serialize the write access to the cache file. The generation of the + // cache file would most likely be executed during index generation, and + // that happens sequencial anyway.... + // Generate the file with a temporary name and move it into place when + // I'm done so I don't have to protect the readers for partially updated + // files... + final File output; + try { + output = File.createTempFile("oghist", null, dir); + try (FileOutputStream out = new FileOutputStream(output); + XMLEncoder e = new XMLEncoder( + new BufferedOutputStream( + new GZIPOutputStream(out)))) { + e.setPersistenceDelegate(File.class, + new FilePersistenceDelegate()); + e.writeObject(history); + } + } catch (IOException ioe) { + throw new HistoryException("Failed to write history", ioe); + } + synchronized (lock) { + if (!cacheFile.delete() && cacheFile.exists()) { + if (!output.delete()) { + LOGGER.log(Level.WARNING, + "Failed to remove temporary history cache file"); + } + throw new HistoryException( + "Cachefile exists, and I could not delete it."); + } + if (!output.renameTo(cacheFile)) { + if (!output.delete()) { + LOGGER.log(Level.WARNING, + "Failed to remove temporary history cache file"); + } + throw new HistoryException("Failed to rename cache tmpfile."); + } + } + } + /** * Store history object (encoded as XML and compressed with gzip) in a file. * @@ -221,10 +271,10 @@ private static History readCache(File file) throws IOException { */ private void storeFile(History histNew, File file, Repository repo) throws HistoryException { - File cache = getCachedFile(file); + File cacheFile = getCachedFile(file); History history = histNew; - File dir = cache.getParentFile(); + File dir = cacheFile.getParentFile(); if (!dir.isDirectory() && !dir.mkdirs()) { throw new HistoryException( "Unable to create cache directory '" + dir + "'."); @@ -233,7 +283,7 @@ private void storeFile(History histNew, File file, Repository repo) throws Histo // Incremental update of the history for this file. History histOld; try { - histOld = readCache(cache); + histOld = readCache(cacheFile); // Merge old history with the new history. List listOld = histOld.getHistoryEntries(); if (!listOld.isEmpty()) { @@ -264,45 +314,7 @@ private void storeFile(History histNew, File file, Repository repo) throws Histo // the data to do it here. } - // We have a problem that multiple threads may access the cache layer - // at the same time. Since I would like to avoid read-locking, I just - // serialize the write access to the cache file. The generation of the - // cache file would most likely be executed during index generation, and - // that happens sequencial anyway.... - // Generate the file with a temporary name and move it into place when - // I'm done so I don't have to protect the readers for partially updated - // files... - final File output; - try { - output = File.createTempFile("oghist", null, dir); - try (FileOutputStream out = new FileOutputStream(output); - XMLEncoder e = new XMLEncoder( - new BufferedOutputStream( - new GZIPOutputStream(out)))) { - e.setPersistenceDelegate(File.class, - new FilePersistenceDelegate()); - e.writeObject(history); - } - } catch (IOException ioe) { - throw new HistoryException("Failed to write history", ioe); - } - synchronized (lock) { - if (!cache.delete() && cache.exists()) { - if (!output.delete()) { - LOGGER.log(Level.WARNING, - "Failed to remove temporary history cache file"); - } - throw new HistoryException( - "Cachefile exists, and I could not delete it."); - } - if (!output.renameTo(cache)) { - if (!output.delete()) { - LOGGER.log(Level.WARNING, - "Failed to remove temporary history cache file"); - } - throw new HistoryException("Failed to rename cache tmpfile."); - } - } + writeHistoryToFile(dir, history, cacheFile); } private void finishStore(Repository repository, String latestRev) { @@ -391,7 +403,7 @@ public void store(History history, Repository repository) for (Map.Entry> map_entry : map.entrySet()) { try { if (env.isHandleHistoryOfRenamedFiles() && - isRenamedFile(map_entry, env, repository, history)) { + isRenamedFile(map_entry.getKey(), env, repository, history)) { continue; } } catch (IOException ex) { @@ -399,7 +411,8 @@ public void store(History history, Repository repository) "isRenamedFile() got exception " , ex); } - doFileHistory(map_entry, env, repository, null, root, false); + doFileHistory(map_entry.getKey(), map_entry.getValue(), + env, repository, null, root, false); } if (!env.isHandleHistoryOfRenamedFiles()) { @@ -414,7 +427,7 @@ public void store(History history, Repository repository) new HashMap<>(); for (final Map.Entry> map_entry : map.entrySet()) { try { - if (isRenamedFile(map_entry, env, repository, history)) { + if (isRenamedFile(map_entry.getKey(), env, repository, history)) { renamed_map.put(map_entry.getKey(), map_entry.getValue()); } } catch (IOException ex) { @@ -442,7 +455,8 @@ public void store(History history, Repository repository) @Override public void run() { try { - doFileHistory(map_entry, env, repositoryF, + doFileHistory(map_entry.getKey(), map_entry.getValue(), + env, repositoryF, new File(env.getSourceRootPath() + map_entry.getKey()), root, true); } catch (Exception ex) { diff --git a/src/org/opensolaris/opengrok/history/MercurialRepository.java b/src/org/opensolaris/opengrok/history/MercurialRepository.java index e025a9535dc..5b64cf2c0a9 100644 --- a/src/org/opensolaris/opengrok/history/MercurialRepository.java +++ b/src/org/opensolaris/opengrok/history/MercurialRepository.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.TreeSet; @@ -161,28 +162,35 @@ Executor getHistoryLogExecutor(File file, String sinceRevision) cmd.add(RepoCommand); cmd.add("log"); - // For plain files we would like to follow the complete history - // (this is necessary for getting the original name in given revision - // when handling renamed files) - if (!file.isDirectory()) { - cmd.add("-f"); - } - - // If this is non-default branch we would like to get the changesets - // on that branch and also any changesets from the parent branch(es). - if (sinceRevision != null) { - cmd.add("-r"); - String[] parts = sinceRevision.split(":"); - if (parts.length == 2) { - cmd.add("reverse(" + parts[0] + "::'" + getBranch() + "')"); + if (file.isDirectory()) { + // If this is non-default branch we would like to get the changesets + // on that branch and also follow any changesets from the parent branch. + if (sinceRevision != null) { + cmd.add("-r"); + String[] parts = sinceRevision.split(":"); + if (parts.length == 2) { + cmd.add("reverse(" + parts[0] + "::'" + getBranch() + "')"); + } else { + throw new HistoryException( + "Don't know how to parse changeset identifier: " + + sinceRevision); + } } else { - throw new HistoryException( - "Don't know how to parse changeset identifier: " - + sinceRevision); + cmd.add("-r"); + cmd.add("reverse(0::'" + getBranch() + "')"); } } else { - cmd.add("-r"); - cmd.add("reverse(0::'" + getBranch() + "')"); + // For plain files we would like to follow the complete history + // (this is necessary for getting the original name in given revision + // when handling renamed files) + // It is not needed to filter on a branch as 'hg log' will follow + // the active branch. + // Due to behavior of recent Mercurial versions, it is not possible + // to filter the changesets of a file based on revision. + // For files this does not matter since if getHistory() is called + // for a file, the file has to be renamed so we want its complete history. + cmd.add("--follow"); + cmd.add(filename); } cmd.add("--template"); @@ -192,9 +200,6 @@ Executor getHistoryLogExecutor(File file, String sinceRevision) /* JDBC requires complete list of files. */ cmd.add(env.storeHistoryCacheInDB() ? FILE_TEMPLATE_LIST : FILE_TEMPLATE); } - if (!filename.isEmpty()) { - cmd.add(filename); - } return new Executor(cmd, new File(directoryName), sinceRevision != null); } @@ -264,7 +269,7 @@ private InputStream getHistoryRev(String fullpath, String rev) { } /** - * Get the name of file in given revision + * Get the name of file in given revision. * * @param fullpath file path * @param full_rev_to_find revision number (in the form of @@ -289,20 +294,20 @@ private String findOriginalName(String fullpath, String full_rev_to_find) /* * Get the list of file renames for given file to the specified * revision. We need to get them from the newest to the oldest - * (hence the reverse()) so that we can follow the renames down - * to the revision we are after. + * so that we can follow the renames down to the revision we are after. */ argv.add(ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK)); argv.add("log"); - argv.add("-f"); + argv.add("--follow"); /* - * hg log -f -r behavior has changed since Mercurial 3.4 so filtering - * the changesets of a file no longer works with -f. + * hg log --follow -r behavior has changed since Mercurial 3.4 + * so filtering the changesets of a file no longer works with --follow. * This is tracked by https://bz.mercurial-scm.org/show_bug.cgi?id=4959 * Once this is fixed and Mercurial versions with the fix are prevalent, * we can revert to the old behavior. */ // argv.add("-r"); + // Use reverse() to get the changesets from newest to oldest. // argv.add("reverse(" + rev_to_find + ":)"); argv.add("--template"); argv.add("{rev}:{file_copies}\\n"); @@ -576,9 +581,17 @@ History getHistory(File file) throws HistoryException { History getHistory(File file, String sinceRevision) throws HistoryException { RuntimeEnvironment env = RuntimeEnvironment.getInstance(); + // Note that the filtering of revisions based on sinceRevision is done + // in the history log executor by passing appropriate options to + // the 'hg' executable. + // This is done only for directories since if getHistory() is used + // for file, the file is renamed and its complete history is fetched + // so no sinceRevision filter is needed. + // See findOriginalName() code for more details. History result = new MercurialHistoryParser(this).parse(file, sinceRevision); - // Assign tags to changesets they represent + + // Assign tags to changesets they represent. // We don't need to check if this repository supports tags, // because we know it :-) if (env.isTagsEnabled()) { diff --git a/test/org/opensolaris/opengrok/history/FileHistoryCacheTest.java b/test/org/opensolaris/opengrok/history/FileHistoryCacheTest.java index 9ae6cc93f43..592c2e75c2d 100644 --- a/test/org/opensolaris/opengrok/history/FileHistoryCacheTest.java +++ b/test/org/opensolaris/opengrok/history/FileHistoryCacheTest.java @@ -41,6 +41,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.opensolaris.opengrok.history.MercurialRepositoryTest.runHgCommand; /** * Test file based history cache with special focus on incremental reindex. @@ -340,15 +341,26 @@ public void testStoreAndGet() throws Exception { cache.get(reposRoot, repo, true).getHistoryEntries(), true); } - /* - * Test what happens when incremental reindex brings in changesets where - * a file is renamed. + /** + * Check how incremental reindex behaves when indexing changesets that + * rename+change file. + * + * The scenario goes as follows: + * - create repo + * - perform full reindex + * - add changesets which renamed and modify a file + * - perform incremental reindex + * - change+rename the file again + * - incremental reindex + * + * @throws Exception */ @ConditionalRun(condition = RepositoryInstalled.MercurialInstalled.class) @Test - public void testRenamedFile() throws Exception { + public void testRenameFileThenDoIncrementalReindex() throws Exception { File reposRoot = new File(repositories.getSourceRoot(), "mercurial"); Repository repo = RepositoryFactory.getRepository(reposRoot); + History updatedHistory; // The test expects support for renamed files. RuntimeEnvironment.getInstance().setHandleHistoryOfRenamedFiles(true); @@ -357,17 +369,22 @@ public void testRenamedFile() throws Exception { cache.store(historyToStore, repo); - // import changesets which rename one of the files + // Import changesets which rename one of the files in the repository. MercurialRepositoryTest.runHgCommand("import", - reposRoot, getClass().getResource("hg-export-renamed.txt").getPath()); + reposRoot, getClass().getResource("hg-export-renamed.txt").getPath()); - // reindex + // Perform incremental reindex. repo.createCache(cache, cache.getLatestCachedRevision(repo)); - // Check changesets for the renames and changes. - File main2 = new File(reposRoot.toString() + File.separatorChar + "main2.c"); - History updatedHistory = cache.get(main2, repo, false); + // Verify size of complete history for the directory. + updatedHistory = cache.get(reposRoot, repo, true); + assertEquals(14, updatedHistory.getHistoryEntries().size()); + + // Check changesets for the renames and changes of single file. + File main2File = new File(reposRoot.toString() + File.separatorChar + "main2.c"); + updatedHistory = cache.get(main2File, repo, false); + // Changesets e0-e3 were brought in by the import done above. HistoryEntry e0 = new HistoryEntry( "13:e55a793086da", new Date(1245447973L / 60 * 60 * 1000), // whole minutes only @@ -408,12 +425,146 @@ public void testRenamedFile() throws Exception { entriesConstruct.add(e4); entriesConstruct.add(e5); histConstruct.setHistoryEntries(entriesConstruct); + assertEquals(6, updatedHistory.getHistoryEntries().size()); assertSameEntries(histConstruct.getHistoryEntries(), - updatedHistory.getHistoryEntries(), false); + updatedHistory.getHistoryEntries(), false); - // Verify size of complete history for the directory. + // Add some changes and rename the file again. + MercurialRepositoryTest.runHgCommand("import", + reposRoot, getClass().getResource("hg-export-renamed-again.txt").getPath()); + + // Perform incremental reindex. + repo.createCache(cache, cache.getLatestCachedRevision(repo)); + + HistoryEntry e6 = new HistoryEntry( + "14:55c41cd4b348", + new Date(1489505558L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "rename + cstyle", + true); + entriesConstruct = new LinkedList<>(); + entriesConstruct.add(e6); + entriesConstruct.add(e0); + entriesConstruct.add(e1); + entriesConstruct.add(e2); + entriesConstruct.add(e3); + entriesConstruct.add(e4); + entriesConstruct.add(e5); + histConstruct.setHistoryEntries(entriesConstruct); + + // Check changesets for the renames and changes of single file. + File main3File = new File(reposRoot.toString() + File.separatorChar + "main3.c"); + updatedHistory = cache.get(main3File, repo, false); + assertEquals(7, updatedHistory.getHistoryEntries().size()); + assertSameEntries(histConstruct.getHistoryEntries(), + updatedHistory.getHistoryEntries(), false); + } + + /** + * Make sure generating incremental history index in branched repository + * with renamed file produces correct history for the renamed file + * (i.e. there should not be history entries from the default branch made + * there after the branch was created). + * + * @throws Exception + */ + @ConditionalRun(condition = RepositoryInstalled.MercurialInstalled.class) + @Test + public void testRenamedFilePlusChangesBranched() throws Exception { + File reposRoot = new File(repositories.getSourceRoot(), "mercurial"); + History updatedHistory; + + // The test expects support for renamed files. + RuntimeEnvironment.getInstance().setHandleHistoryOfRenamedFiles(true); + + // Branch the repo and add one changeset. + runHgCommand("unbundle", + reposRoot, getClass().getResource("hg-branch.bundle").getPath()); + + // Import changesets which rename one of the files in the default branch. + runHgCommand("import", + reposRoot, getClass().getResource("hg-export-renamed.txt").getPath()); + + // Switch to the newly created branch. + runHgCommand("update", reposRoot, "mybranch"); + + // Generate history index. + Repository repo = RepositoryFactory.getRepository(reposRoot); + History historyToStore = repo.getHistory(reposRoot); + cache.store(historyToStore, repo); + + /* quick sanity check */ updatedHistory = cache.get(reposRoot, repo, true); - assertEquals(14, updatedHistory.getHistoryEntries().size()); + assertEquals(11, updatedHistory.getHistoryEntries().size()); + + // Import changesets which rename the file in the new branch. + runHgCommand("import", + reposRoot, getClass().getResource("hg-export-renamed-branched.txt").getPath()); + + // Perform incremental reindex. + repo.createCache(cache, cache.getLatestCachedRevision(repo)); + + /* overall history check */ + updatedHistory = cache.get(reposRoot, repo, false); + assertEquals(12, updatedHistory.getHistoryEntries().size()); + + // Check complete list of history entries for the renamed file. + File testFile = new File(reposRoot.toString() + File.separatorChar + "blog.txt"); + updatedHistory = cache.get(testFile, repo, false); + + HistoryEntry e0 = new HistoryEntry( + "15:709c7a27f9fa", + new Date(1489160275L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "novels are so last century. Let's write a blog !", + true); + HistoryEntry e1 = new HistoryEntry( + "10:c4518ca0c841", + new Date(1415483555L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "branched", + true); + HistoryEntry e2 = new HistoryEntry( + "8:6a8c423f5624", + new Date(1362586899L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "first words of the novel", + true); + HistoryEntry e3 = new HistoryEntry( + "7:db1394c05268", + new Date(1362586862L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "book sounds too boring, let's do a novel !", + true); + HistoryEntry e4 = new HistoryEntry( + "6:e386b51ddbcc", + new Date(1362586839L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "stub of chapter 1", + true); + HistoryEntry e5 = new HistoryEntry( + "5:8706402863c6", + new Date(1362586805L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "I decided to actually start writing a book based on the first plaintext file.", + true); + HistoryEntry e6 = new HistoryEntry( + "4:e494d67af12f", + new Date(1362586747L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "first change", + true); + HistoryEntry e7 = new HistoryEntry( + "3:2058725c1470", + new Date(1362586483L / 60 * 60 * 1000), // whole minutes only + "Vladimir Kotal ", null, "initial checking of text files", + true); + + History histConstruct = new History(); + LinkedList entriesConstruct = new LinkedList<>(); + entriesConstruct.add(e0); + entriesConstruct.add(e1); + entriesConstruct.add(e2); + entriesConstruct.add(e3); + entriesConstruct.add(e4); + entriesConstruct.add(e5); + entriesConstruct.add(e6); + entriesConstruct.add(e7); + histConstruct.setHistoryEntries(entriesConstruct); + assertSameEntries(histConstruct.getHistoryEntries(), + updatedHistory.getHistoryEntries(), false); } private void checkNoHistoryFetchRepo(String reponame, String filename, diff --git a/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java b/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java index 98af0bf47a1..c46c13ea3b1 100644 --- a/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java +++ b/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java @@ -117,7 +117,7 @@ public void testGetHistory() throws Exception { /** * Test that subset of changesets can be extracted based on penultimate - * revision number. + * revision number. This works for directories only. * * @throws Exception */ @@ -213,7 +213,7 @@ public void testGetHistoryBranch() throws Exception { } /** - * Test that it is possible to get contents of last revision of a text file. + * Test that contents of last revision of a text file match expected content. * * @throws java.lang.Exception */ @@ -245,6 +245,11 @@ public void testGetHistoryGet() throws Exception { assertEquals(exp_str, str); } + /** + * Test that it is possible to get contents of multiple revisions of a file. + * + * @throws java.lang.Exception + */ @Test public void testgetHistoryGetForAll() throws Exception { setUpTestRepository(); diff --git a/test/org/opensolaris/opengrok/history/hg-export-renamed-again.txt b/test/org/opensolaris/opengrok/history/hg-export-renamed-again.txt new file mode 100644 index 00000000000..97c5094cb1a --- /dev/null +++ b/test/org/opensolaris/opengrok/history/hg-export-renamed-again.txt @@ -0,0 +1,32 @@ +# HG changeset patch +# User Vladimir Kotal +# Date 1489505558 -3600 +# Tue Mar 14 16:32:38 2017 +0100 +# Node ID 55c41cd4b3482a96d0f343abff50d9cb4b9048f6 +# Parent e55a793086da74a1095f92bac968e3683a233c3a +rename + cstyle + +diff --git a/main2.c b/main3.c +rename from main2.c +rename to main3.c +--- a/main2.c ++++ b/main3.c +@@ -1,12 +1,13 @@ + #include "header.h" + +-int main(int argc, char **argv) { +- +- (void)printf("Program %s executed with the following arguments:\n", argv[0]); ++int ++main(int argc, char **argv) ++{ ++ (void) printf("Program %s executed with the following arguments:\n", argv[0]); + for (int i = 1; i < argc; ++i) { +- (void)printf("[%s] ", argv[i]); ++ (void) printf("[%s] ", argv[i]); + } +- (void)printf("\n"); ++ (void) printf("\n"); + + return argc == 1 ? EXIT_SUCCESS : EXIT_FAILURE; + } diff --git a/test/org/opensolaris/opengrok/history/hg-export-renamed-branched.txt b/test/org/opensolaris/opengrok/history/hg-export-renamed-branched.txt new file mode 100644 index 00000000000..74920d1dee0 --- /dev/null +++ b/test/org/opensolaris/opengrok/history/hg-export-renamed-branched.txt @@ -0,0 +1,22 @@ +# HG changeset patch +# User Vladimir Kotal +# Date 1489160275 -3600 +# Fri Mar 10 16:37:55 2017 +0100 +# Branch mybranch +# Node ID 709c7a27f9faf0f71762c813d896efbe2a791b4b +# Parent c4518ca0c841c910575d098cbb4b947bfbe8b9a8 +novels are so last century. Let's write a blog ! + +diff --git a/novel.txt b/blog.txt +rename from novel.txt +rename to blog.txt +--- a/novel.txt ++++ b/blog.txt +@@ -1,6 +1,4 @@ +-This will be a novel based on the previous novel. +- +-Chapter 1. ++This will be a blog ! + + Let's write some words. It began like this: + From deeaeb7c8bace42df453120e096bfe159e880a33 Mon Sep 17 00:00:00 2001 From: Vladimir Kotal Date: Wed, 15 Mar 2017 10:29:06 +0100 Subject: [PATCH 2/4] reverse --- opengrok-web-nbproject/nbproject/project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opengrok-web-nbproject/nbproject/project.properties b/opengrok-web-nbproject/nbproject/project.properties index b86e1658e5d..b32b18fbf46 100644 --- a/opengrok-web-nbproject/nbproject/project.properties +++ b/opengrok-web-nbproject/nbproject/project.properties @@ -34,7 +34,7 @@ endorsed.classpath= excludes= file.reference.bcel-6.0.jar=../lib/bcel-6.0.jar file.reference.json-simple-1.1.1.jar=../lib/json-simple-1.1.1.jar -j2ee.platform.classpath=${j2ee.server.home}/lib/annotations-api.jar:${j2ee.server.home}/lib/catalina-ant.jar:${j2ee.server.home}/lib/catalina-ha.jar:${j2ee.server.home}/lib/catalina-storeconfig.jar:${j2ee.server.home}/lib/catalina-tribes.jar:${j2ee.server.home}/lib/catalina.jar:${j2ee.server.home}/lib/ecj-4.5.jar:${j2ee.server.home}/lib/el-api.jar:${j2ee.server.home}/lib/jasper-el.jar:${j2ee.server.home}/lib/jasper.jar:${j2ee.server.home}/lib/jsp-api.jar:${j2ee.server.home}/lib/servlet-api.jar:${j2ee.server.home}/lib/tomcat-api.jar:${j2ee.server.home}/lib/tomcat-coyote.jar:${j2ee.server.home}/lib/tomcat-dbcp.jar:${j2ee.server.home}/lib/tomcat-i18n-es.jar:${j2ee.server.home}/lib/tomcat-i18n-fr.jar:${j2ee.server.home}/lib/tomcat-i18n-ja.jar:${j2ee.server.home}/lib/tomcat-jdbc.jar:${j2ee.server.home}/lib/tomcat-jni.jar:${j2ee.server.home}/lib/tomcat-util-scan.jar:${j2ee.server.home}/lib/tomcat-util.jar:${j2ee.server.home}/lib/tomcat-websocket.jar:${j2ee.server.home}/lib/websocket-api.jar +j2ee.platform.classpath=${j2ee.server.home}/lib/annotations-api.jar:${j2ee.server.home}/lib/catalina-ant.jar:${j2ee.server.home}/lib/catalina-ha.jar:${j2ee.server.home}/lib/catalina-storeconfig.jar:${j2ee.server.home}/lib/catalina-tribes.jar:${j2ee.server.home}/lib/catalina.jar:${j2ee.server.home}/lib/ecj-4.4.2.jar:${j2ee.server.home}/lib/el-api.jar:${j2ee.server.home}/lib/jasper-el.jar:${j2ee.server.home}/lib/jasper.jar:${j2ee.server.home}/lib/jsp-api.jar:${j2ee.server.home}/lib/servlet-api.jar:${j2ee.server.home}/lib/tomcat-api.jar:${j2ee.server.home}/lib/tomcat-coyote.jar:${j2ee.server.home}/lib/tomcat-dbcp.jar:${j2ee.server.home}/lib/tomcat-i18n-es.jar:${j2ee.server.home}/lib/tomcat-i18n-fr.jar:${j2ee.server.home}/lib/tomcat-i18n-ja.jar:${j2ee.server.home}/lib/tomcat-jdbc.jar:${j2ee.server.home}/lib/tomcat-jni.jar:${j2ee.server.home}/lib/tomcat-util-scan.jar:${j2ee.server.home}/lib/tomcat-util.jar:${j2ee.server.home}/lib/tomcat-websocket.jar:${j2ee.server.home}/lib/websocket-api.jar lucene.version=6.4.1 lucene-core.jar=lucene-core-${lucene.version}.jar lucene-analyzers-common.jar=lucene-analyzers-common-${lucene.version}.jar From 003bb9b4fd91a697cb2b278bdf1478fcc6696644 Mon Sep 17 00:00:00 2001 From: Vladimir Kotal Date: Wed, 15 Mar 2017 10:33:27 +0100 Subject: [PATCH 3/4] copyright --- src/org/opensolaris/opengrok/history/FileHistoryCache.java | 2 +- .../opensolaris/opengrok/history/MercurialRepositoryTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/opensolaris/opengrok/history/FileHistoryCache.java b/src/org/opensolaris/opengrok/history/FileHistoryCache.java index 637fcd23b7a..8e10c438094 100644 --- a/src/org/opensolaris/opengrok/history/FileHistoryCache.java +++ b/src/org/opensolaris/opengrok/history/FileHistoryCache.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. */ package org.opensolaris.opengrok.history; diff --git a/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java b/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java index c46c13ea3b1..d2c6a0b4d9f 100644 --- a/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java +++ b/test/org/opensolaris/opengrok/history/MercurialRepositoryTest.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. */ package org.opensolaris.opengrok.history; From b68e3b0ce4467cfab5420851784b06555dedd527 Mon Sep 17 00:00:00 2001 From: Vladimir Kotal Date: Wed, 15 Mar 2017 10:43:41 +0100 Subject: [PATCH 4/4] add comment --- src/org/opensolaris/opengrok/history/MercurialRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/opensolaris/opengrok/history/MercurialRepository.java b/src/org/opensolaris/opengrok/history/MercurialRepository.java index 5b64cf2c0a9..d17f4ec2cb1 100644 --- a/src/org/opensolaris/opengrok/history/MercurialRepository.java +++ b/src/org/opensolaris/opengrok/history/MercurialRepository.java @@ -144,7 +144,8 @@ String determineBranch() throws IOException { * * @param file The file or directory to retrieve history for * @param sinceRevision the oldest changeset to return from the executor, or - * {@code null} if all changesets should be returned + * {@code null} if all changesets should be returned. + * For files this does not apply and full history is returned. * @return An Executor ready to be started */ Executor getHistoryLogExecutor(File file, String sinceRevision)