Mercurial > jhg
comparison hg4j/src/main/java/org/tmatesoft/hg/repo/HgRepository.java @ 213:6ec4af642ba8 gradle
Project uses Gradle for build - actual changes
| author | Alexander Kitaev <kitaev@gmail.com> |
|---|---|
| date | Tue, 10 May 2011 10:52:53 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 212:edb2e2829352 | 213:6ec4af642ba8 |
|---|---|
| 1 /* | |
| 2 * Copyright (c) 2010-2011 TMate Software Ltd | |
| 3 * | |
| 4 * This program is free software; you can redistribute it and/or modify | |
| 5 * it under the terms of the GNU General Public License as published by | |
| 6 * the Free Software Foundation; version 2 of the License. | |
| 7 * | |
| 8 * This program is distributed in the hope that it will be useful, | |
| 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 * GNU General Public License for more details. | |
| 12 * | |
| 13 * For information on how to redistribute this software under | |
| 14 * the terms of a license other than GNU General Public License | |
| 15 * contact TMate Software at support@hg4j.com | |
| 16 */ | |
| 17 package org.tmatesoft.hg.repo; | |
| 18 | |
| 19 import java.io.File; | |
| 20 import java.io.IOException; | |
| 21 import java.lang.ref.SoftReference; | |
| 22 import java.util.ArrayList; | |
| 23 import java.util.Collections; | |
| 24 import java.util.HashMap; | |
| 25 import java.util.List; | |
| 26 | |
| 27 import org.tmatesoft.hg.internal.ConfigFile; | |
| 28 import org.tmatesoft.hg.internal.DataAccessProvider; | |
| 29 import org.tmatesoft.hg.internal.Filter; | |
| 30 import org.tmatesoft.hg.internal.RelativePathRewrite; | |
| 31 import org.tmatesoft.hg.internal.RequiresFile; | |
| 32 import org.tmatesoft.hg.internal.RevlogStream; | |
| 33 import org.tmatesoft.hg.util.FileIterator; | |
| 34 import org.tmatesoft.hg.util.FileWalker; | |
| 35 import org.tmatesoft.hg.util.Path; | |
| 36 import org.tmatesoft.hg.util.PathRewrite; | |
| 37 | |
| 38 | |
| 39 | |
| 40 /** | |
| 41 * Shall be as state-less as possible, all the caching happens outside the repo, in commands/walkers | |
| 42 * | |
| 43 * @author Artem Tikhomirov | |
| 44 * @author TMate Software Ltd. | |
| 45 */ | |
| 46 public final class HgRepository { | |
| 47 | |
| 48 // if new constants added, consider fixing HgInternals#wrongLocalRevision | |
| 49 public static final int TIP = -3; | |
| 50 public static final int BAD_REVISION = Integer.MIN_VALUE; | |
| 51 public static final int WORKING_COPY = -2; | |
| 52 | |
| 53 // temp aux marker method | |
| 54 public static IllegalStateException notImplemented() { | |
| 55 return new IllegalStateException("Not implemented"); | |
| 56 } | |
| 57 | |
| 58 private final File repoDir; // .hg folder | |
| 59 private final String repoLocation; | |
| 60 private final DataAccessProvider dataAccess; | |
| 61 private final PathRewrite normalizePath; | |
| 62 private final PathRewrite dataPathHelper; | |
| 63 private final PathRewrite repoPathHelper; | |
| 64 | |
| 65 private HgChangelog changelog; | |
| 66 private HgManifest manifest; | |
| 67 private HgTags tags; | |
| 68 // XXX perhaps, shall enable caching explicitly | |
| 69 private final HashMap<Path, SoftReference<RevlogStream>> streamsCache = new HashMap<Path, SoftReference<RevlogStream>>(); | |
| 70 | |
| 71 private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals(); | |
| 72 private HgIgnore ignore; | |
| 73 private ConfigFile configFile; | |
| 74 | |
| 75 HgRepository(String repositoryPath) { | |
| 76 repoDir = null; | |
| 77 repoLocation = repositoryPath; | |
| 78 dataAccess = null; | |
| 79 dataPathHelper = repoPathHelper = null; | |
| 80 normalizePath = null; | |
| 81 } | |
| 82 | |
| 83 HgRepository(String repositoryPath, File repositoryRoot) { | |
| 84 assert ".hg".equals(repositoryRoot.getName()) && repositoryRoot.isDirectory(); | |
| 85 assert repositoryPath != null; | |
| 86 assert repositoryRoot != null; | |
| 87 repoDir = repositoryRoot; | |
| 88 repoLocation = repositoryPath; | |
| 89 dataAccess = new DataAccessProvider(); | |
| 90 final boolean runningOnWindows = System.getProperty("os.name").indexOf("Windows") != -1; | |
| 91 if (runningOnWindows) { | |
| 92 normalizePath = new PathRewrite() { | |
| 93 | |
| 94 public String rewrite(String path) { | |
| 95 // TODO handle . and .. (although unlikely to face them from GUI client) | |
| 96 path = path.replace('\\', '/').replace("//", "/"); | |
| 97 if (path.startsWith("/")) { | |
| 98 path = path.substring(1); | |
| 99 } | |
| 100 return path; | |
| 101 } | |
| 102 }; | |
| 103 } else { | |
| 104 normalizePath = new PathRewrite.Empty(); // or strip leading slash, perhaps? | |
| 105 } | |
| 106 parseRequires(); | |
| 107 dataPathHelper = impl.buildDataFilesHelper(); | |
| 108 repoPathHelper = impl.buildRepositoryFilesHelper(); | |
| 109 } | |
| 110 | |
| 111 @Override | |
| 112 public String toString() { | |
| 113 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]"; | |
| 114 } | |
| 115 | |
| 116 public String getLocation() { | |
| 117 return repoLocation; | |
| 118 } | |
| 119 | |
| 120 public boolean isInvalid() { | |
| 121 return repoDir == null || !repoDir.exists() || !repoDir.isDirectory(); | |
| 122 } | |
| 123 | |
| 124 public HgChangelog getChangelog() { | |
| 125 if (this.changelog == null) { | |
| 126 String storagePath = repoPathHelper.rewrite("00changelog.i"); | |
| 127 RevlogStream content = resolve(Path.create(storagePath), true); | |
| 128 this.changelog = new HgChangelog(this, content); | |
| 129 } | |
| 130 return this.changelog; | |
| 131 } | |
| 132 | |
| 133 public HgManifest getManifest() { | |
| 134 if (this.manifest == null) { | |
| 135 RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true); | |
| 136 this.manifest = new HgManifest(this, content); | |
| 137 } | |
| 138 return this.manifest; | |
| 139 } | |
| 140 | |
| 141 public final HgTags getTags() { | |
| 142 if (tags == null) { | |
| 143 tags = new HgTags(); | |
| 144 try { | |
| 145 tags.readGlobal(new File(repoDir.getParentFile(), ".hgtags")); | |
| 146 tags.readLocal(new File(repoDir, "localtags")); | |
| 147 } catch (IOException ex) { | |
| 148 ex.printStackTrace(); // FIXME log or othewise report | |
| 149 } | |
| 150 } | |
| 151 return tags; | |
| 152 } | |
| 153 | |
| 154 public HgDataFile getFileNode(String path) { | |
| 155 String nPath = normalizePath.rewrite(path); | |
| 156 String storagePath = dataPathHelper.rewrite(nPath); | |
| 157 RevlogStream content = resolve(Path.create(storagePath), false); | |
| 158 Path p = Path.create(nPath); | |
| 159 if (content == null) { | |
| 160 return new HgDataFile(this, p); | |
| 161 } | |
| 162 return new HgDataFile(this, p, content); | |
| 163 } | |
| 164 | |
| 165 public HgDataFile getFileNode(Path path) { | |
| 166 String storagePath = dataPathHelper.rewrite(path.toString()); | |
| 167 RevlogStream content = resolve(Path.create(storagePath), false); | |
| 168 // XXX no content when no file? or HgDataFile.exists() to detect that? | |
| 169 if (content == null) { | |
| 170 return new HgDataFile(this, path); | |
| 171 } | |
| 172 return new HgDataFile(this, path, content); | |
| 173 } | |
| 174 | |
| 175 /* clients need to rewrite path from their FS to a repository-friendly paths, and, perhaps, vice versa*/ | |
| 176 public PathRewrite getToRepoPathHelper() { | |
| 177 return normalizePath; | |
| 178 } | |
| 179 | |
| 180 // local to hide use of io.File. | |
| 181 /*package-local*/ File getRepositoryRoot() { | |
| 182 return repoDir; | |
| 183 } | |
| 184 | |
| 185 // XXX package-local, unless there are cases when required from outside (guess, working dir/revision walkers may hide dirstate access and no public visibility needed) | |
| 186 /*package-local*/ final HgDirstate loadDirstate() { | |
| 187 return new HgDirstate(getDataAccess(), new File(repoDir, "dirstate")); | |
| 188 } | |
| 189 | |
| 190 // package-local, see comment for loadDirstate | |
| 191 /*package-local*/ final HgIgnore getIgnore() { | |
| 192 // TODO read config for additional locations | |
| 193 if (ignore == null) { | |
| 194 ignore = new HgIgnore(); | |
| 195 try { | |
| 196 File ignoreFile = new File(repoDir.getParentFile(), ".hgignore"); | |
| 197 ignore.read(ignoreFile); | |
| 198 } catch (IOException ex) { | |
| 199 ex.printStackTrace(); // log warn | |
| 200 } | |
| 201 } | |
| 202 return ignore; | |
| 203 } | |
| 204 | |
| 205 /*package-local*/ DataAccessProvider getDataAccess() { | |
| 206 return dataAccess; | |
| 207 } | |
| 208 | |
| 209 // FIXME not sure repository shall create walkers | |
| 210 /*package-local*/ FileIterator createWorkingDirWalker() { | |
| 211 File repoRoot = repoDir.getParentFile(); | |
| 212 Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(repoRoot), getToRepoPathHelper())); | |
| 213 // Impl note: simple source is enough as files in the working dir are all unique | |
| 214 // even if they might get reused (i.e. after FileIterator#reset() and walking once again), | |
| 215 // path caching is better to be done in the code which knows that path are being reused | |
| 216 return new FileWalker(repoRoot, pathSrc); | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * Perhaps, should be separate interface, like ContentLookup | |
| 221 * path - repository storage path (i.e. one usually with .i or .d) | |
| 222 */ | |
| 223 /*package-local*/ RevlogStream resolve(Path path, boolean shallFakeNonExistent) { | |
| 224 final SoftReference<RevlogStream> ref = streamsCache.get(path); | |
| 225 RevlogStream cached = ref == null ? null : ref.get(); | |
| 226 if (cached != null) { | |
| 227 return cached; | |
| 228 } | |
| 229 File f = new File(repoDir, path.toString()); | |
| 230 if (f.exists()) { | |
| 231 RevlogStream s = new RevlogStream(dataAccess, f); | |
| 232 streamsCache.put(path, new SoftReference<RevlogStream>(s)); | |
| 233 return s; | |
| 234 } else { | |
| 235 if (shallFakeNonExistent) { | |
| 236 try { | |
| 237 File fake = File.createTempFile(f.getName(), null); | |
| 238 fake.deleteOnExit(); | |
| 239 return new RevlogStream(dataAccess, fake); | |
| 240 } catch (IOException ex) { | |
| 241 ex.printStackTrace(); // FIXME report in debug | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 return null; // XXX empty stream instead? | |
| 246 } | |
| 247 | |
| 248 // can't expose internal class, otherwise seems reasonable to have it in API | |
| 249 /*package-local*/ ConfigFile getConfigFile() { | |
| 250 if (configFile == null) { | |
| 251 configFile = impl.newConfigFile(); | |
| 252 configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc")); | |
| 253 // last one, overrides anything else | |
| 254 // <repo>/.hg/hgrc | |
| 255 configFile.addLocation(new File(getRepositoryRoot(), "hgrc")); | |
| 256 } | |
| 257 return configFile; | |
| 258 } | |
| 259 | |
| 260 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) { | |
| 261 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); | |
| 262 } | |
| 263 | |
| 264 /*package-local*/ List<Filter> getFiltersFromWorkingDirToRepo(Path p) { | |
| 265 return instantiateFilters(p, new Filter.Options(Filter.Direction.ToRepo)); | |
| 266 } | |
| 267 | |
| 268 private List<Filter> instantiateFilters(Path p, Filter.Options opts) { | |
| 269 List<Filter.Factory> factories = impl.getFilters(this, getConfigFile()); | |
| 270 if (factories.isEmpty()) { | |
| 271 return Collections.emptyList(); | |
| 272 } | |
| 273 ArrayList<Filter> rv = new ArrayList<Filter>(factories.size()); | |
| 274 for (Filter.Factory ff : factories) { | |
| 275 Filter f = ff.create(p, opts); | |
| 276 if (f != null) { | |
| 277 rv.add(f); | |
| 278 } | |
| 279 } | |
| 280 return rv; | |
| 281 } | |
| 282 | |
| 283 private void parseRequires() { | |
| 284 new RequiresFile().parse(impl, new File(repoDir, "requires")); | |
| 285 } | |
| 286 | |
| 287 } |
