aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/blame
Commit message (Collapse)AuthorAgeFilesLines
* Revert "Option to pass start RevCommit to be blamed on to theMatthias Sohn2022-09-061-34/+13
| | | | | | | | | | | | BlameGenerator." This reverts commit 5747bba48b22a11beba8ebe0caf13a53d4ca96f2. This is done as a quick fix for the failure of egit tests caused by the introduction of FilteredRevCommit. Bug: 580690 Change-Id: Ia0178bc2de4fc825a81207bbd7979bf3a386c955
* Option to pass start RevCommit to be blamed on to the BlameGenerator.Ronald Bhuleskar2022-08-171-13/+34
| | | | | | | | | This can allow passing a FilteredRevCommit which is the filtered list of commit graph making it easier for Blame to work on. This can significantly improve blame performance since blame can skip expensive RevWalk. Change-Id: Ie127cb710d004079e9f53a5802130afdb49a7de1
* Revert "Option to pass start RevCommit to be blamed on to the BlameGenerator."Ronald Bhuleskar2022-08-031-34/+13
| | | | | | | | | | This reverts commit 59e8bec6e7705a89b5d0b9c6ac004b323ffa16b0. Reason for revert: The change in https://git.eclipse.org/r/c/jgit/jgit/+/194354 broke the egit test [1]. Calling c.getShortMessage() causes an NPE. [1] https://ci.eclipse.org/egit/job/egit.gerrit/2711/ Change-Id: Iaf5feb35f4bb4c3487b04be15d1fe11376975523
* Option to pass start RevCommit to be blamed on to the BlameGenerator.Ronald Bhuleskar2022-08-021-13/+34
| | | | | | | | | This can allow passing a FilteredRevCommit which is the filtered list of commit graph making it easier for Blame to work on. This can significantly improve blame performance since blame can skip expensive RevWalk. Change-Id: I5dab25301d6aef7df6a0bc25a4c553c730199272
* JGit blame very slow for large merge commits that rename filesSimeon Andreev2022-07-011-3/+5
| | | | | | | | | | | | Adjusted BlameGenerator to filter rename detection with the blame path. This reduces the running time of the blame computation significantly, for repositories with massive commits involving renames. The filtered rename detection is made (internally) available with: org.eclipse.jgit.internal.diff.FilteredRenameDetector Bug: 578900 Change-Id: I6580004e81102d685081b8180da1587a35073d36 Signed-off-by: Simeon Andreev <simeon.danailov.andreev@gmail.com>
* Javadoc fixesThomas Wolf2021-11-222-7/+7
| | | | | | | | | Skip javadoc generation for test bundles. Use character entities &lt; and &gt; for < and > outside of code-formatted spans. Change-Id: I66e1a1dc98881c61f93c9e5561c5513896b2ba01 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
* Update EDL 1.0 license headers to new short SPDX compliant formatMatthias Sohn2020-01-045-190/+25
| | | | | | | | | | This is the format given by the Eclipse legal doc generator [1]. [1] https://www.eclipse.org/projects/tools/documentation.php?id=technology.jgit Bug: 548298 Change-Id: I8d8cabc998ba1b083e3f0906a8d558d391ffb6c4 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Replace chain of if statements with switchCarsten Hammer2019-12-141-3/+7
| | | | | | | | | | | and switch over strings where possible. Sometimes if statements are chained and form a series of comparisons against constants. Using switch statements improves readability. Bug: 545856 Change-Id: Iacb78956ee5c20db4d793e6b668508ec67466606 Signed-off-by: Carsten Hammer <carsten.hammer@t-online.de> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Make blame work correctly on merge conflictsThomas Wolf2019-11-272-2/+179
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a conflicting file was blamed, JGit would not identify lines coming from the merge parents. The main cause for this was that Blame and BlameCommand simply added the first DirCacheEntry found for a file to its queue of candidates (blobs or commits) to consider. In case of a conflict this typically is the merge base commit, and comparing a auto-merged contents against that base would yield incorrect results. Such cases have to be handled specially. The candidate to be considered by the blame must use the working tree contents, but at the same time behave like a merge commit/candidate with HEAD and the MERGE_HEADs as parents. Canonical git does something very similar, see [1]. Implement that and add tests. I first did this for the JGit pgm Blame command. When I then tried to do the same in BlameCommand, I noticed that the latter also included some fancy but incomplete CR-LF handling. In order to be able to use the new BlameGenerator.prepareHead() also in BlameCommand this CR-LF handling was also moved into BlameGenerator and corrected in doing so. (Just considering the git config settings was not good enough, CR-LF behavior can also be influenced by .gitattributes, and even by whether the file in the index has CR-LF. To correctly determine CR-LF handling for check-in one needs to do a TreeWalk with at least a FileTreeIterator and a DirCacheIterator.) [1] https://github.com/git/git/blob/v2.22.0/blame.c#L174 Bug: 434330 Change-Id: I9d763dd6ba478b0b6ebf9456049d6301f478ef7c Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
* Enable and fix "Statement unnecessarily nested within else clause" warningsDavid Pursehouse2019-10-171-5/+5
| | | | | | | | | | | | | | | Since [1] the gerrit project includes jgit as a submodule, and has this warning enabled, resulting in 100s of warnings in the console. Also enable the warning here, and fix them. At the same time, add missing braces around adjacent and nearby one-line blocks. [1] https://gerrit-review.googlesource.com/c/gerrit/+/227897 Change-Id: I81df3fc7ed6eedf6874ce1a3bedfa727a1897e4c Signed-off-by: David Pursehouse <david.pursehouse@gmail.com>
* Fix API breakage introduced by da254106Matthias Sohn2018-08-081-20/+6
| | | | | | | | | | | Use org.eclipse.jgit.errors.CancelledException which is a subclass of IOException instead of org.eclipse.jgit.api.errors.CanceledException in order to avoid breaking API. We can reconsider this with the next major version 6.0. Bug: 536324 Change-Id: Ia6f84f59aa6b7d78b8fccaba24ade320a54f7458 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
* Abort rename detection in a timely manner if cancelledMatthias Sohn2018-08-071-6/+20
| | | | | | | | If progress monitor is cancelled break loops in rename detection by throwing a CanceledException. Bug: 536324 Change-Id: Ia3511fb749d2a5d45005e72c156b874ab7a0da26 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Fix NPE in BlameGenerator.getSourceStart()Matthias Sohn2018-07-241-1/+1
| | | | | Bug: 499543 Change-Id: I99f6ebb1c3ceea20e8ca093acbe824c9f0362d45 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Remove further unnecessary 'final' keywordsHan-Wen Nienhuys2018-05-181-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Remove it from * package private functions. * try blocks * for loops this was done with the following python script: $ cat f.py import sys import re import os def replaceFinal(m): return m.group(1) + "(" + m.group(2).replace('final ', '') + ")" methodDecl = re.compile(r"^([\t ]*[a-zA-Z_ ]+)\(([^)]*)\)") def subst(fn): input = open(fn) os.rename(fn, fn + "~") dest = open(fn, 'w') for l in input: l = methodDecl.sub(replaceFinal, l) dest.write(l) dest.close() for root, dirs, files in os.walk(".", topdown=False): for f in files: if not f.endswith('.java'): continue full = os.path.join(root, f) print full subst(full) Change-Id: If533a75a417594fc893e7c669d2c1f0f6caeb7ca Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
* LFS: support merge/rebase/cherry-pick/diff/compare with LFS filesMarkus Duft2018-03-032-19/+34
| | | | | | | | | | | | Respect merge=lfs and diff=lfs attributes where required to replace (in memory) the content of LFS pointers with the actual blob content from the LFS storage (and vice versa when staging/merging). Does not implement general support for merge/diff attributes for any other use case apart from LFS. Change-Id: Ibad8875de1e0bee8fe3a1dffb1add93111534cae Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Fix javadoc in org.eclipse.jgit blame packageMatthias Sohn2017-12-175-31/+100
| | | | Change-Id: I116dacc7f4ace341ec6ffae2ab96b53496d89f64 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* BlameGenerator: Annotate #getRenameDetector as NullableDavid Pursehouse2017-02-091-3/+6
| | | | | | | | | | | | | The renameDetector member returned by this method will be null when following file renames has been disabled by previously calling: setFollowFileRenames(false). Annotate it as @Nullable and update the Javadoc to explicitly document the null return. Change-Id: I9bdf443a64cf3c45352d3ab023051a2e11f7426d Signed-off-by: David Pursehouse <david.pursehouse@gmail.com>
* Replace deprecated release() methods by close()Matthias Sohn2015-05-211-8/+0
| | | | | | | | See the discussion [1] in the Gerrit mailing list. [1] https://groups.google.com/forum/#!topic/repo-discuss/RRQT_xCqz4o Change-Id: I2c67384309c5c2e8511a7d0d4e088b4e95f819ff Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Use AutoClosable to close resources in bundle org.eclipse.jgitMatthias Sohn2015-05-211-3/+3
| | | | | | | - use try-with-resource where possible - replace use of deprecated release() by close() Change-Id: I0f139c3535679087b7fa09649166bca514750b81 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* ObjectWalk: make setRetainBody(false) the defaultShawn Pearce2015-05-101-1/+0
| | | | | | | | | | | | | | | Despite being the primary author of RevWalk and ObjectWalk I still fail to remember to setRetainBody(false) in application code using an ObjectWalk to examine the graph. Document the default for RevWalk is setRetainBody(true), where the application usually wants the commit bodies to display or inspect. Change the default for ObjectWalk to setRetainBody(false), as nearly all callers want only the graph shape and do not need the larger text inside a commit body. This allows some code in JGit to be simplified. Change-Id: I367e42209e805bd5e1f41b4072aeb2fa98ec9d99
* Replace use of deprecated release() method in BlameResultMatthias Sohn2015-04-021-4/+4
| | | | Change-Id: Ic555e6e0443337aed694bc6445a60abe08e07ad2 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* Implement AutoClosable interface on classes that used release()Matthias Sohn2015-02-091-3/+16
| | | | | | | | | Implement AutoClosable and deprecate the old release() method to give JGit consumers some time to adapt. Bug: 428039 Change-Id: Id664a91dc5a8cf2ac401e7d87ce2e3b89e221458 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
* blame: Un-break isFile check in tree walkJonathan Nieder2014-06-041-1/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Originally, blame's walk to find a scapegoat to blame for a file walking backward from a commit used the test treeWalk.getFileMode(0).getObjectType() != OBJ_BLOB to throw out gitlink (submodule) entries. Later, 52500d3264d2 (blame: Micro optimize blob lookup in tree, 2014-04-17) changed that test to (treeWalk.getRawMode(0) & TYPE_FILE) != TYPE_FILE These checks are not the same, though: the older test accepts files and symlinks, while the newer one accepts files, symlinks, and gitlink (submodule) entries. This is particularly broken in the submodule case --- trying to parse the referred-to commit as a blob produces caught an exception: GET /gerrit/+blame/master/plugins/reviewnotes HTTP/1.1 org.eclipse.jgit.errors.MissingObjectException: Missing blob 61702414c046dd6b811c9137b765f9db422f83db Stick to just (possibly executable) files instead. Symlinks are not line-oriented data so blame on a symlink is not likely to be useful. A quick grep for '& TYPE_' doesn't find any other instances of this bug. Change-Id: Iebcc91f1bee3c91adda51dccd6372e8302bf23fe Signed-off-by: Jonathan Nieder <jrn@google.com>
* blame: Revert common subtree elimination "optimization"Shawn Pearce2014-05-192-42/+3
| | | | | | | | | | | | | | | | This partially reverts 6de12836d72fe4cba9afc297c8988c6fff851fa9. Performing a TreeWalk over 2 trees to identify and skip unmodified subtrees to pass all blame onto an ancestor appears to be a micro optimization that works for a very limited number of files. In the general case the 2 tree walk is slowing down blame more than it helps to speed it up. I keep coming up with files in multiple repositories where 6de128 is making things worse, not better, and only one example where it actually improved performance, render_view_impl.cc in chromium as described in the commit message. Change-Id: Ic6d5fff22acb5ab6485614a07bdb388e8c336679
* blame: Fix merges, where merge result differs only by whitespaceKonrad Kügler2014-05-031-2/+3
| | | | | | | | | | | | | | | | | | | When blaming a merge commit with "Ignore whitespace changes" enabled, don't discard blame candidates for other parents when we encounter a parent that only has whitespace changes compared to the merge result. The algorithm early prepares parents for blaming, removing the appropriate blame regions from the list of regions still to blame. Only at the end, the prepared blame candidates are submitted for blaming. When looking at a non-first parent which only differs in whitespace to the merge result, it submitted that parent, but only to blame it for the (usually few) lines not already prepared to blame on other parents. Due to an early return the blame candidates for the previous parents were forgotten, leaving many lines unannotated. bug: 433024 Change-Id: I43c9caf2078b92b05e652dbed2192568907bf199 Signed-off-by: Konrad Kügler <swamblumat-eclipsebugs@yahoo.de>
* Fix BlameResult.computeRange JavaDoc to exclude end indexKonrad Kügler2014-04-261-2/+2
| | | | Change-Id: Ifd7dd86e3848eb7a522ba5c49d5c216777484f3b Signed-off-by: Konrad Kügler <swamblumat-eclipsebugs@yahoo.de>
* blame: Reuse existing blameEntireRegionOnParent methodShawn Pearce2014-04-211-3/+1
| | | | | | | | Skipping directly to the parent is already possible with an existing helper method. Update the source path (to follow the rename) and then use the existing code path to push the parent inside the current entry. Change-Id: Icb1d49e53d14b599efc478990613625a9e058e09
* blame: Remove unnecessary curly braces around single statement ifShawn Pearce2014-04-211-2/+1
| | | | Change-Id: I8bcab44785fe08bbf3519c634e57ebfea8d3f0f9
* blame: Allow candidate to manage its setup before outputShawn Pearce2014-04-212-2/+12
| | | | | | | | Pass in the RevWalk and let the candidate decide how to prepare itself for output. This removes the conditional for the missing sourceCommit, as candidates missing a commit can override the method with a no-op. Change-Id: I3fa19b8676dfd3c177583f8f42593b5000b5350d
* blame: Do not update candidate regionList during outputShawn Pearce2014-04-211-21/+24
| | | | | | | | Instead of updating the candidate's regionList field to iterate through the linked list of regions, use a special purpose field in the BlameGenerator. This allows the candidate to be unmodified. Change-Id: I2cda031b59220ab603ef82050e741ecbbaa1953f
* blame: Only use computeRange if -L was requestedShawn Pearce2014-04-211-0/+4
| | | | | | | The computeRange method is inefficient for computing the entire file. If the entire file was selected ask for the entire file. Change-Id: I8b2dbf635e875cc125443dac50be121208646540
* blame: Reduce running time ~4.5% by skipping common subtreesShawn Pearce2014-04-172-17/+60
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | With this commit running blame on render_view_impl.cc[1] saves about 644 ms over prior versions, reducing the time about 4.5%. Large projects often contain strands of commits where no changes are made to a particular subtree. Blame used to dive recursively into these subtrees to look for the blob and check if its SHA-1 was changed. In chromium/src[1] only 20% of the commits modify the content/renderer subtree relevant for the file. The recursivePath is necessary to check for '/' and remember if common subtree elimination should be attempted. When a file lives within a subtree the extra cost to check for unmodified subtrees saves time. However for files in the root tree the extra work incurred by TreeWalk is not worthwhile and would significantly increase overall running time. Now typical running times from an otherwise idle desktop: real 0m13.387s 0m13.341s 0m13.443s user 0m15.410s 0m15.220s 0m15.350s previously: real 0m14.085s 0m14.049s 0m13.968s user 0m15.730s 0m15.820s 0m15.770s [1] https://chromium.googlesource.com/chromium/src/+show/34d6e5c5b4248b1b199405af7ad00f961921f347/content/renderer/render_view_impl.cc Change-Id: Ib16d684df7ffa034ee28def3fb22c797998d5b7b
* blame: Micro optimize blob lookup in treeShawn Pearce2014-04-171-10/+8
| | | | | | | | | | | | | Avoid converting the raw mode to FileMode. This is an expensive if-else-if sort of test to just check if the thing is a blob. Instead test the bit mask directly, which is at least a few instructions shorter. The TreeWalk is already recursive and will auto-dive into any subtrees found. isSubtree check is unnecessary, as is the loop, as only one result will ever be returned by next(). Change-Id: I9fb25229ebed857469427bfbdf74aedebfddfac8
* blame: Automatically increase commit abbreviation lengthShawn Pearce2014-04-171-0/+12
| | | | | | | | | Ensure commit object names are unique by extending the default abbreviation as long as necessary. This allows `jgit blame` to more closely match the formatted output of `git blame` on large histories like Gerrit Code Review's ReceiveCommits.java file. Change-Id: I5f7c4855769ee9dcba973389df9e109005dcdb5b
* Blame correctly in the presence of conflicting mergesKonrad Kügler2014-04-172-19/+77
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: The BlameGenerator used the RevFlag SEEN to mark commits it had already looked at (but not necessarily processed), to prevent processing a commit multiple times. If a commit is a conflicting merge that contains lines of the merge base, that have been deleted in its first parent, either these lines or the lines untouched since the merge base would not be blamed properly. This happens for example if a file is modified on a main branch in an earlier commit M and on a side branch in a later commit S. For this example, M deletes some lines relative to the common base commit B, and S modifies a subset of these lines, leaving some other of these lines untouched. Then side is merged into main, creating a conflict for these lines. The merge resolution shall carry over some unmodified lines from B that would otherwise be deleted by M. The route to blame these lines is via S to B. They can't be blamed via M, as they don't exist there anymore. Q |\ | \ | S | | M | | / |/ B Blaming the merged file first blames via S, because that is the most recent commit. Doing so, it also looks at B to blame the unmodified lines of B carried over by S into the merge result. In the course of this, B is submitted for later processing and marked SEEN. Later M is blamed. It notices that its parent commit B has been SEEN and aborts processing for M. B is blamed after that, but only for the lines that survived via S. As a result, only the lines contributed by S or by B via S are blamed. All the other lines that were unchanges by both M and S, which should have been blamed to B via M, are not blamed. Solution: Don't abort processing when encountering a SEEN commit. Rather add the new region list of lines to be blamed to those of the already SEEN and enqueued commit's region list. This way when the B commit of the above example is processed, it will blame both the lines of M and S, yielding a complete blame result. Bug: 374382 Change-Id: I369059597608022948009ea7708cc8190f05a8d3 Signed-off-by: Konrad Kügler <swamblumat-eclipsebugs@yahoo.de> Signed-off-by: Shawn Pearce <spearce@spearce.org>
* Mark non-externalizable strings as suchRobin Rosenberg2012-12-273-4/+5
| | | | | | | | | | A few classes such as Constanrs are marked with @SuppressWarnings, as are toString() methods with many liternal, but otherwise $NLS-n$ is used for string containing text that should not be translated. A few literals may fall into the gray zone, but mostly I've tried to only tag the obvious ones. Change-Id: I22e50a77e2bf9e0b842a66bdf674e8fa1692f590
* Merge "[blame] Don't pass null to PersonIdent constructor"Shawn Pearce2012-11-011-1/+1
|\
| * [blame] Don't pass null to PersonIdent constructorRobin Stocker2012-10-291-1/+1
| | | | | | | | | | | | | | | | The API was changed to not allow null values anymore with I0ac994ae8e47789d38f7c6e6db55d482f0f1bac3, leading to an IAE. Bug: 393054 Change-Id: If33560ae976b46a02ff75b2e4ec05c13a8ad2d41
* | Add Javadoc description for packagesRobin Stocker2012-10-311-0/+4
|/ | | | | | | | | These appear as descriptions in the index, see here (currently empty): http://download.eclipse.org/jgit/docs/latest/apidocs/ Change-Id: If7996deef30ae688bade8b3ad6b19547ca3d8b50 Signed-off-by: Chris Aniszczyk <zx@twitter.com>
* Make BlameGenerator comments more clearPatrick Carlson2012-08-171-4/+6
| | | | | | | | | The file location of the constructor for BlameGenerator did not specify where the path should be relative from. Fix BlameGenerator comments based on suggestions by Robin Stocker. Change-Id: I3d79db2d2ba4961835fe664ae6178e0bfc97b910
* Move JGitText to an internal packageRobin Rosenberg2012-03-121-1/+1
| | | | Change-Id: I763590a45d75f00a09097ab6f89581a3bbd3c797
* [blame] Fix blame following renames in non-toplevel directoriesCarsten Pfeiffer2011-11-101-0/+1
| | | | | | | | | | | Mark the treeWalk as recursive; otherwise following renames only works for toplevel files. Bug: 302549 Change-Id: I70867928eadf332b0942f8bf6877a3acb3828c87 Signed-off-by: Carsten Pfeiffer <carsten.pfeiffer@gebit.de> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: Chris Aniszczyk <zx@twitter.com>
* [blame] Reset rename detector before computing renames.Kevin Sawicki2011-09-011-0/+1
| | | | | | Bug: 354507 Change-Id: I5e9c65a082d9dee1e87536c5cf2a8de75efa6a33 Signed-off-by: Kevin Sawicki <kevin@github.com>
* blame: Compute the origin of lines in a result fileShawn O. Pearce2011-05-315-0/+1948
BlameGenerator digs through history and discovers the origin of each line of some result file. BlameResult consumes the stream of regions created by the generator and lays them out in a table for applications to display alongside of source lines. Applications may optionally push in the working tree copy of a file using the push(String, byte[]) method, allowing the application to receive accurate line annotations for the working tree version. Lines that are uncommitted (difference between HEAD and working tree) will show up with the description given by the application as the author, or "Not Committed Yet" as a default string. Applications may also run the BlameGenerator in reverse mode using the reverse(AnyObjectId, AnyObjectId) method instead of push(). When running in the reverse mode the generator annotates lines by the commit they are removed in, rather than the commit they were added in. This allows a user to discover where a line disappeared from when they are looking at an older revision in the repository. For example: blame --reverse 16e810b2..master -L 1080, org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/RefDirectoryTest.java ( 1080) } 2302a6d3 (Christian Halstrick 2011-05-20 11:18:20 +0200 1081) 2302a6d3 (Christian Halstrick 2011-05-20 11:18:20 +0200 1082) /** 2302a6d3 (Christian Halstrick 2011-05-20 11:18:20 +0200 1083) * Kick the timestamp of a local file. Above we learn that line 1080 (a closing curly brace of the prior method) still exists in branch master, but the Javadoc comment below it has been removed by Christian Halstrick on May 20th as part of commit 2302a6d3. This result differs considerably from that of C Git's blame --reverse feature. JGit tells the reader which commit performed the delete, while C Git tells the reader the last commit that still contained the line, leaving it an exercise to the reader to discover the descendant that performed the removal. This is still only a basic implementation. Quite notably it is missing support for the smart block copy/move detection that the C implementation of `git blame` is well known for. Despite being incremental, the BlameGenerator can only be run once. After the generator runs it cannot be reused. A better implementation would support applications browsing through history efficiently. In regards to CQ 5110, only a little of the original code survives. CQ: 5110 Bug: 306161 Change-Id: I84b8ea4838bb7d25f4fcdd540547884704661b8f Signed-off-by: Kevin Sawicki <kevin@github.com> Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>