# HG changeset patch # User Artem Tikhomirov # Date 1297883621 -3600 # Node ID 44b97930570cee5b59b1863672b3e652278c2d14 # Parent 2e395db595e2df149e31a4807facdb3623db8bff Introduced ChangelogHelper to look up changesets files were modified in diff -r 2e395db595e2 -r 44b97930570c TODO --- a/TODO Wed Feb 16 18:42:10 2011 +0100 +++ b/TODO Wed Feb 16 20:13:41 2011 +0100 @@ -41,6 +41,14 @@ + \r\n <==> \n - force translation if inconsistent (now either fails or does nothing) +* API + - rename in .core Cset -> HgChangeset, + - rename in .repo Changeset to HgChangelog.Changeset, Changeset.Inspector -> HgChangelog.Inspector + - CommandContext + - Data access - not bytes, but ByteChannel + +* defects + ConfigFile to strip comments from values (#) Proposed: - LogCommand.revision(int... rev)+ to walk selected revisions only (list->sort(array) on execute, binary search) @@ -50,7 +58,6 @@ - hgignore: read extra ignore files from config file (ui.ignore) - Read-only support, version 1.1 ============================== Committed: @@ -59,3 +66,5 @@ * incoming * outgoing + +- clone remote repo diff -r 2e395db595e2 -r 44b97930570c design.txt --- a/design.txt Wed Feb 16 18:42:10 2011 +0100 +++ b/design.txt Wed Feb 16 20:13:41 2011 +0100 @@ -42,7 +42,9 @@ DataAccess - collect debug info (buffer misses, file size/total read operations) to find out better strategy to buffer size detection. Compare performance. Strip off metadata from beg of the stream - DataAccess (with rebase/moveBaseOffset(int)) would be handy -Parameterize StatusCollector to produce copy only when needed. And HgDataFile.metadata perhaps should be moved to cacheable place? +Parameterize StatusCollector to produce copy only when needed. And HgDataFile.metadata perhaps should be moved to cacheable place? + +RevisionMap to replace TreeMap Status operation from GUI - guess, usually on a file/subfolder, hence API should allow for starting path (unlike cmdline, seems useless to implement include/exclide patterns - GUI users hardly enter them, ever) -> recently introduced FileWalker may perhaps help solving this (if starts walking from selected folder) for status op against WorkingDir? diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/core/HgStatus.java --- a/src/org/tmatesoft/hg/core/HgStatus.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatus.java Wed Feb 16 20:13:41 2011 +0100 @@ -12,10 +12,15 @@ * * For information on how to redistribute this software under * the terms of a license other than GNU General Public License - * contact TMate Software at support@svnkit.com + * contact TMate Software at support@hg4j.com */ package org.tmatesoft.hg.core; +import java.util.Date; + +import org.tmatesoft.hg.internal.ChangelogHelper; +import org.tmatesoft.hg.repo.Changeset; + public class HgStatus { public enum Kind { @@ -25,15 +30,17 @@ private final HgStatus.Kind kind; private final Path path; private final Path origin; + private final ChangelogHelper logHelper; - HgStatus(HgStatus.Kind kind, Path path) { - this(kind, path, null); + HgStatus(HgStatus.Kind kind, Path path, ChangelogHelper changelogHelper) { + this(kind, path, null, changelogHelper); } - HgStatus(HgStatus.Kind kind, Path path, Path copyOrigin) { + HgStatus(HgStatus.Kind kind, Path path, Path copyOrigin, ChangelogHelper changelogHelper) { this.kind = kind; this.path = path; origin = copyOrigin; + logHelper = changelogHelper; } public HgStatus.Kind getKind() { @@ -51,10 +58,29 @@ public boolean isCopy() { return origin != null; } - -// public String getModificationAuthor() { -// } -// -// public Date getModificationDate() { -// } + + /** + * @return null if author for the change can't be deduced (e.g. for clean files it's senseless) + */ + public String getModificationAuthor() { + Changeset cset = logHelper.findLatestChangeWith(path); + if (cset == null) { + if (kind == Kind.Modified || kind == Kind.Added || kind == Kind.Removed /*&& RightBoundary is TIP*/) { + return logHelper.getNextCommitUsername(); + } + } else { + return cset.user(); + } + return null; + } + + public Date getModificationDate() { + Changeset cset = logHelper.findLatestChangeWith(path); + if (cset == null) { + // FIXME check dirstate and/or local file for tstamp + return new Date(); // what's correct + } else { + return cset.date(); + } + } } \ No newline at end of file diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/core/StatusCommand.java --- a/src/org/tmatesoft/hg/core/StatusCommand.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/core/StatusCommand.java Wed Feb 16 20:13:41 2011 +0100 @@ -22,6 +22,7 @@ import java.util.ConcurrentModificationException; import org.tmatesoft.hg.core.Path.Matcher; +import org.tmatesoft.hg.internal.ChangelogHelper; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; import org.tmatesoft.hg.repo.HgStatusInspector; @@ -39,7 +40,6 @@ private int endRevision = WORKING_COPY; private boolean visitSubRepo = true; - private Handler handler; private final Mediator mediator = new Mediator(); public StatusCommand(HgRepository hgRepo) { @@ -145,16 +145,15 @@ if (statusHandler == null) { throw new IllegalArgumentException(); } - if (handler != null) { + if (mediator.busy()) { throw new ConcurrentModificationException(); } - handler = statusHandler; HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext // PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext try { // XXX if I need a rough estimation (for ProgressMonitor) of number of work units, // I may use number of files in either rev1 or rev2 manifest edition - mediator.start(); + mediator.start(statusHandler, new ChangelogHelper(repo, startRevision)); if (endRevision == WORKING_COPY) { HgWorkingCopyStatusCollector wcsc = new HgWorkingCopyStatusCollector(repo); wcsc.setBaseRevisionCollector(sc); @@ -168,7 +167,6 @@ } } finally { mediator.done(); - handler = null; } } @@ -186,69 +184,79 @@ boolean needIgnored; boolean needCopies; Matcher matcher; + Handler handler; + private ChangelogHelper logHelper; Mediator() { } - public void start() { - + public void start(Handler h, ChangelogHelper changelogHelper) { + handler = h; + logHelper = changelogHelper; } + public void done() { + handler = null; + logHelper = null; + } + + public boolean busy() { + return handler != null; } public void modified(Path fname) { if (needModified) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Modified, fname)); + handler.handleStatus(new HgStatus(Modified, fname, logHelper)); } } } public void added(Path fname) { if (needAdded) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Added, fname)); + handler.handleStatus(new HgStatus(Added, fname, logHelper)); } } } public void removed(Path fname) { if (needRemoved) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Removed, fname)); + handler.handleStatus(new HgStatus(Removed, fname, logHelper)); } } } public void copied(Path fnameOrigin, Path fnameAdded) { if (needCopies) { if (matcher == null || matcher.accept(fnameAdded)) { - handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin)); + handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); } } } public void missing(Path fname) { if (needMissing) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Missing, fname)); + handler.handleStatus(new HgStatus(Missing, fname, logHelper)); } } } public void unknown(Path fname) { if (needUnknown) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Unknown, fname)); + handler.handleStatus(new HgStatus(Unknown, fname, logHelper)); } } } public void clean(Path fname) { if (needClean) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Clean, fname)); + handler.handleStatus(new HgStatus(Clean, fname, logHelper)); } } } public void ignored(Path fname) { if (needIgnored) { if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Ignored, fname)); + handler.handleStatus(new HgStatus(Ignored, fname, logHelper)); } } } diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/internal/ChangelogHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/ChangelogHelper.java Wed Feb 16 20:13:41 2011 +0100 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.internal; + +import java.util.TreeMap; + +import org.tmatesoft.hg.core.Path; +import org.tmatesoft.hg.repo.Changeset; +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgRepository; + +/** + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class ChangelogHelper { + private final int leftBoundary; + private final HgRepository repo; + private final TreeMap cache = new TreeMap(); + + /** + * @param hgRepo + * @param leftBoundaryRevision walker never visits revisions with local numbers less than specified, + * IOW only revisions [leftBoundaryRevision..TIP] are considered. + */ + public ChangelogHelper(HgRepository hgRepo, int leftBoundaryRevision) { + repo = hgRepo; + leftBoundary = leftBoundaryRevision; + } + + /** + * @return the repo + */ + public HgRepository getRepo() { + return repo; + } + + /** + * Walks changelog in reverse order + * @param file + * @return changeset where specified file is mentioned among affected files, or + * null if none found up to leftBoundary + */ + public Changeset findLatestChangeWith(Path file) { + HgDataFile df = repo.getFileNode(file); + int changelogRev = df.getChangesetLocalRevision(HgRepository.TIP); + if (changelogRev >= leftBoundary) { + // the method is likely to be invoked for different files, + // while changesets might be the same. Cache 'em not to read too much. + Changeset cs = cache.get(changelogRev); + if (cs == null) { + cs = repo.getChangelog().range(changelogRev, changelogRev).get(0); + cache.put(changelogRev, cs); + } + return cs; + } + return null; + } + + public String getNextCommitUsername() { + return new HgInternals(repo).getNextCommitUsername(); + } +} diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/internal/ConfigFile.java --- a/src/org/tmatesoft/hg/internal/ConfigFile.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/ConfigFile.java Wed Feb 16 20:13:41 2011 +0100 @@ -84,6 +84,11 @@ } return false; } + + public String getString(String sectionName, String key, String defaultValue) { + String value = getSection(sectionName).get(key); + return value == null ? defaultValue : value; + } // TODO handle %include and %unset directives // TODO "" and lists diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/internal/KeywordFilter.java --- a/src/org/tmatesoft/hg/internal/KeywordFilter.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/internal/KeywordFilter.java Wed Feb 16 20:13:41 2011 +0100 @@ -268,6 +268,7 @@ private Changeset getChangeset() { if (latestFileCset == null) { + // XXX consider use of ChangelogHelper int csetRev = repo.getFileNode(path).getChangesetLocalRevision(HgRepository.TIP); latestFileCset = repo.getChangelog().range(csetRev, csetRev).get(0); } diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/repo/Changeset.java --- a/src/org/tmatesoft/hg/repo/Changeset.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/Changeset.java Wed Feb 16 20:13:41 2011 +0100 @@ -202,6 +202,7 @@ String _comment; try { _comment = new String(data, breakIndex4+2, bufferEndIndex - breakIndex4 - 2, "UTF-8"); + // FIXME respect ui.fallbackencoding and try to decode if set } catch (UnsupportedEncodingException ex) { _comment = ""; throw new IllegalStateException("Could hardly happen"); diff -r 2e395db595e2 -r 44b97930570c src/org/tmatesoft/hg/repo/HgInternals.java --- a/src/org/tmatesoft/hg/repo/HgInternals.java Wed Feb 16 18:42:10 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgInternals.java Wed Feb 16 20:13:41 2011 +0100 @@ -17,6 +17,8 @@ package org.tmatesoft.hg.repo; import java.io.File; +import java.net.InetAddress; +import java.net.UnknownHostException; import org.tmatesoft.hg.internal.ConfigFile; @@ -58,4 +60,27 @@ public ConfigFile getRepoConfig() { return repo.getConfigFile(); } + + // in fact, need a setter for this anyway, shall move to internal.Internals perhaps? + public String getNextCommitUsername() { + String hgUser = System.getenv("HGUSER"); + if (hgUser != null && hgUser.trim().length() > 0) { + return hgUser.trim(); + } + String configValue = getRepoConfig().getString("ui", "username", null); + if (configValue != null) { + return configValue; + } + String email = System.getenv("EMAIL"); + if (email != null && email.trim().length() > 0) { + return email; + } + String username = System.getProperty("user.name"); + try { + String hostname = InetAddress.getLocalHost().getHostName(); + return username + '@' + hostname; + } catch (UnknownHostException ex) { + return username; + } + } }