|
19 | 19 |
|
20 | 20 | /*
|
21 | 21 | * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
|
22 |
| - * Portions Copyright (c) 2017-2019, Chris Fraire <[email protected]>. |
| 22 | + * Portions Copyright (c) 2017-2020, Chris Fraire <[email protected]>. |
23 | 23 | * Portions Copyright (c) 2019, Krystof Tulinger <[email protected]>.
|
24 | 24 | */
|
25 | 25 | package org.opengrok.indexer.history;
|
|
36 | 36 | import java.util.ArrayList;
|
37 | 37 | import java.util.Arrays;
|
38 | 38 | import java.util.Date;
|
39 |
| -import java.util.LinkedList; |
40 | 39 | import java.util.List;
|
41 | 40 | import java.util.TreeSet;
|
42 | 41 | import java.util.logging.Level;
|
@@ -345,8 +344,7 @@ String findOriginalName(String fullpath, String changeset)
|
345 | 344 | int status = executor.exec();
|
346 | 345 |
|
347 | 346 | String originalFile = null;
|
348 |
| - try (BufferedReader in = new BufferedReader( |
349 |
| - new InputStreamReader(executor.getOutputStream()))) { |
| 347 | + try (BufferedReader in = new BufferedReader(newLogReader(executor.getOutputStream()))) { |
350 | 348 | String line;
|
351 | 349 | String rev = null;
|
352 | 350 | Matcher m;
|
@@ -385,7 +383,7 @@ String findOriginalName(String fullpath, String changeset)
|
385 | 383 | * Get first revision of given file without following renames.
|
386 | 384 | * @param fullpath file path to get first revision of
|
387 | 385 | */
|
388 |
| - private String getFirstRevision(String fullpath) throws IOException { |
| 386 | + private String getFirstRevision(String fullpath) { |
389 | 387 | String[] argv = {
|
390 | 388 | ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK),
|
391 | 389 | "rev-list",
|
@@ -568,68 +566,74 @@ boolean hasFileBasedTags() {
|
568 | 566 | return true;
|
569 | 567 | }
|
570 | 568 |
|
571 |
| - private TagEntry buildTagEntry(File directory, String tag, boolean interactive) { |
572 |
| - ArrayList<String> argv = new ArrayList<>(); |
573 |
| - ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK); |
574 |
| - argv.add(RepoCommand); |
575 |
| - argv.add("log"); |
576 |
| - argv.add("--format=commit:%H%nDate:%at"); |
577 |
| - argv.add("-n"); |
578 |
| - argv.add("1"); |
579 |
| - argv.add(tag); |
580 |
| - argv.add("--"); |
581 |
| - |
582 |
| - Executor executor = new Executor(argv, directory, interactive ? |
583 |
| - RuntimeEnvironment.getInstance().getInteractiveCommandTimeout() : |
584 |
| - RuntimeEnvironment.getInstance().getCommandTimeout()); |
585 |
| - GitTagParser parser = new GitTagParser(tag); |
586 |
| - executor.exec(true, parser); |
587 |
| - return parser.getEntries().first(); |
588 |
| - } |
589 |
| - |
590 | 569 | @Override
|
591 | 570 | protected void buildTagList(File directory, boolean interactive) {
|
592 | 571 | this.tagList = new TreeSet<>();
|
| 572 | + |
| 573 | + /* |
| 574 | + * Bulk log-tags command courtesy of GitHub's louie0817. |
| 575 | + */ |
593 | 576 | ArrayList<String> argv = new ArrayList<>();
|
594 |
| - LinkedList<String> tagsList = new LinkedList<>(); |
595 | 577 | ensureCommand(CMD_PROPERTY_KEY, CMD_FALLBACK);
|
596 | 578 | argv.add(RepoCommand);
|
597 |
| - argv.add("tag"); |
598 |
| - |
| 579 | + argv.add("log"); |
| 580 | + argv.add("--tags"); |
| 581 | + argv.add("--simplify-by-decoration"); |
| 582 | + argv.add("--pretty=%H:%at:%D:"); |
| 583 | + |
599 | 584 | Executor executor = new Executor(argv, directory, interactive ?
|
600 | 585 | RuntimeEnvironment.getInstance().getInteractiveCommandTimeout() :
|
601 | 586 | RuntimeEnvironment.getInstance().getCommandTimeout());
|
602 |
| - int status = executor.exec(); |
603 |
| - |
604 |
| - try { |
605 |
| - // First we have to obtain list of all tags, and put it aside |
606 |
| - // Otherwise we can't use git to get date & hash for each tag |
607 |
| - try (BufferedReader in = new BufferedReader(new InputStreamReader( |
608 |
| - executor.getOutputStream()))) { |
609 |
| - String line; |
610 |
| - while ((line = in.readLine()) != null) { |
611 |
| - tagsList.add(line); |
612 |
| - } |
613 |
| - } |
614 |
| - } catch (IOException e) { |
615 |
| - LOGGER.log(Level.WARNING, |
616 |
| - "Failed to read tag list: {0}", e.getMessage()); |
617 |
| - this.tagList = null; |
| 587 | + int status = executor.exec(true, new GitTagParser(this.tagList)); |
| 588 | + if (LOGGER.isLoggable(Level.FINEST)) { |
| 589 | + LOGGER.log(Level.FINEST, "Read tags count={0} for {1}", |
| 590 | + new Object[] {tagList.size(), directory}); |
618 | 591 | }
|
619 | 592 |
|
620 | 593 | if (status != 0) {
|
621 | 594 | LOGGER.log(Level.WARNING,
|
622 | 595 | "Failed to get tags for: \"{0}\" Exit code: {1}",
|
623 | 596 | new Object[]{directory.getAbsolutePath(), String.valueOf(status)});
|
624 |
| - this.tagList = null; |
625 |
| - } |
626 |
| - |
627 |
| - // Now get hash & date for each tag. |
628 |
| - for (String tag : tagsList) { |
629 |
| - TagEntry tagEntry = buildTagEntry(directory, tag, interactive); |
630 |
| - // Reverse the order of the list |
631 |
| - this.tagList.add(tagEntry); |
| 597 | + // In case of partial success, do not null-out tagList here. |
632 | 598 | }
|
| 599 | + |
| 600 | + /* |
| 601 | + * Note that the former algorithm ran `git tag` first. `git tag` lists |
| 602 | + * tags in lexicographic order, so the former algorithm with its final |
| 603 | + * reverse() would have produced a result that was reverse- |
| 604 | + * lexicographically ordered. |
| 605 | + * |
| 606 | + * Repository technically relies on the tag list to be reverse-ancestor |
| 607 | + * ordered. |
| 608 | + * |
| 609 | + * This algorithm by using `git log --tags` results in reverse-ancestor |
| 610 | + * order. But there is also a technicality that Repository |
| 611 | + * searches ancestry by using TagEntry.compareTo(HistoryEntry). For |
| 612 | + * an SCM that uses "linear revision numbering" (e.g. SVN or Mercurial), |
| 613 | + * the search is reliable. |
| 614 | + * |
| 615 | + * For GitTagEntry.compareTo(HistoryEntry) that compares by date, the |
| 616 | + * search can technically terminate too early if ancestor-order is not |
| 617 | + * monotonic by date (which Git allows with no complaint). |
| 618 | + * |
| 619 | + * Linus Torvalds: [When asking] "'can commit X be an ancestor of |
| 620 | + * commit Y' (as a way to basically limit certain algorithms from |
| 621 | + * having to walk all the way down). We've used commit dates for it, |
| 622 | + * and realistically it really has worked very well. But it was always |
| 623 | + * a broken heuristic." |
| 624 | + * |
| 625 | + * "I think the lack of [generation numbers] is literally the only real |
| 626 | + * design mistake we have [in Git]." |
| 627 | + * |
| 628 | + * "We discussed adding generation numbers about 6 years ago [in 2005]. |
| 629 | + * We clearly *should* have done it. Instead, we went with the hacky |
| 630 | + * `let's use commit time', that everybody really knew was technically |
| 631 | + * wrong, and was a hack, but avoided the need." |
| 632 | + * |
| 633 | + * If Git ever gets standard generation numbers, |
| 634 | + * GitTagEntry.compareTo() should be revised to work reliably akin to |
| 635 | + * an SCM that uses "linear revision numbering." |
| 636 | + */ |
633 | 637 | }
|
634 | 638 |
|
635 | 639 | @Override
|
|
0 commit comments