Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgRepository.java @ 493:ba36f66c32b4
Refactor to keep knowledge about repository control files and their location in respect to .hg/ in a single place (facilitate future adoption of shared repositories)
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 18 Oct 2012 18:36:13 +0200 |
| parents | 4a670f76e7d1 |
| children | d2f6ab541330 |
comparison
equal
deleted
inserted
replaced
| 492:e4eaa23e3442 | 493:ba36f66c32b4 |
|---|---|
| 97 } | 97 } |
| 98 | 98 |
| 99 private final File repoDir; // .hg folder | 99 private final File repoDir; // .hg folder |
| 100 private final File workingDir; // .hg/../ | 100 private final File workingDir; // .hg/../ |
| 101 private final String repoLocation; | 101 private final String repoLocation; |
| 102 private final PathRewrite normalizePath; // normalized slashes but otherwise regular file names | 102 /* |
| 103 private final PathRewrite dataPathHelper; // access to file storage area (usually under .hg/store/data/), with filenames mangled | 103 * normalized slashes but otherwise regular file names |
| 104 private final PathRewrite repoPathHelper; // access to system files | 104 * the only front-end path rewrite, kept here as rest of the library shall |
| 105 * not bother with names normalization. | |
| 106 */ | |
| 107 private final PathRewrite normalizePath; | |
| 105 private final SessionContext sessionContext; | 108 private final SessionContext sessionContext; |
| 106 | 109 |
| 107 private HgChangelog changelog; | 110 private HgChangelog changelog; |
| 108 private HgManifest manifest; | 111 private HgManifest manifest; |
| 109 private HgTags tags; | 112 private HgTags tags; |
| 129 | 132 |
| 130 HgRepository(String repositoryPath) { | 133 HgRepository(String repositoryPath) { |
| 131 repoDir = null; | 134 repoDir = null; |
| 132 workingDir = null; | 135 workingDir = null; |
| 133 repoLocation = repositoryPath; | 136 repoLocation = repositoryPath; |
| 134 dataPathHelper = repoPathHelper = null; | |
| 135 normalizePath = null; | 137 normalizePath = null; |
| 136 sessionContext = null; | 138 sessionContext = null; |
| 137 impl = null; | 139 impl = null; |
| 138 } | 140 } |
| 139 | 141 |
| 151 throw new IllegalArgumentException(repoDir.toString()); | 153 throw new IllegalArgumentException(repoDir.toString()); |
| 152 } | 154 } |
| 153 repoLocation = repositoryPath; | 155 repoLocation = repositoryPath; |
| 154 sessionContext = ctx; | 156 sessionContext = ctx; |
| 155 impl = new org.tmatesoft.hg.internal.Internals(this, repositoryRoot); | 157 impl = new org.tmatesoft.hg.internal.Internals(this, repositoryRoot); |
| 156 impl.parseRequires(); | |
| 157 normalizePath = impl.buildNormalizePathRewrite(); | 158 normalizePath = impl.buildNormalizePathRewrite(); |
| 158 dataPathHelper = impl.buildDataFilesHelper(); | |
| 159 repoPathHelper = impl.buildRepositoryFilesHelper(); | |
| 160 } | 159 } |
| 161 | 160 |
| 162 @Override | 161 @Override |
| 163 public String toString() { | 162 public String toString() { |
| 164 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]"; | 163 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]"; |
| 183 return impl == null || impl.isInvalid(); | 182 return impl == null || impl.isInvalid(); |
| 184 } | 183 } |
| 185 | 184 |
| 186 public HgChangelog getChangelog() { | 185 public HgChangelog getChangelog() { |
| 187 if (changelog == null) { | 186 if (changelog == null) { |
| 188 CharSequence storagePath = repoPathHelper.rewrite("00changelog.i"); | 187 File chlogFile = impl.getFileFromStoreDir("00changelog.i"); |
| 189 RevlogStream content = resolve(Path.create(storagePath), true); | 188 if (!chlogFile.exists()) { |
| 189 // fake its existence | |
| 190 chlogFile = fakeNonExistentFile(chlogFile); | |
| 191 } | |
| 192 RevlogStream content = new RevlogStream(impl.getDataAccess(), chlogFile); | |
| 190 changelog = new HgChangelog(this, content); | 193 changelog = new HgChangelog(this, content); |
| 191 } | 194 } |
| 192 return changelog; | 195 return changelog; |
| 193 } | 196 } |
| 194 | 197 |
| 195 public HgManifest getManifest() { | 198 public HgManifest getManifest() { |
| 196 if (manifest == null) { | 199 if (manifest == null) { |
| 197 RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true); | 200 File manifestFile = impl.getFileFromStoreDir("00manifest.i"); |
| 201 if (!manifestFile.exists()) { | |
| 202 manifestFile = fakeNonExistentFile(manifestFile); | |
| 203 } | |
| 204 RevlogStream content = new RevlogStream(impl.getDataAccess(), manifestFile); | |
| 198 manifest = new HgManifest(this, content, impl.buildFileNameEncodingHelper()); | 205 manifest = new HgManifest(this, content, impl.buildFileNameEncodingHelper()); |
| 199 } | 206 } |
| 200 return manifest; | 207 return manifest; |
| 201 } | 208 } |
| 202 | 209 |
| 265 return mergeState; | 272 return mergeState; |
| 266 } | 273 } |
| 267 | 274 |
| 268 public HgDataFile getFileNode(String path) { | 275 public HgDataFile getFileNode(String path) { |
| 269 CharSequence nPath = normalizePath.rewrite(path); | 276 CharSequence nPath = normalizePath.rewrite(path); |
| 270 CharSequence storagePath = dataPathHelper.rewrite(nPath); | |
| 271 RevlogStream content = resolve(Path.create(storagePath), false); | |
| 272 Path p = Path.create(nPath); | 277 Path p = Path.create(nPath); |
| 273 if (content == null) { | 278 return getFileNode(p); |
| 274 return new HgDataFile(this, p); | |
| 275 } | |
| 276 return new HgDataFile(this, p, content); | |
| 277 } | 279 } |
| 278 | 280 |
| 279 public HgDataFile getFileNode(Path path) { | 281 public HgDataFile getFileNode(Path path) { |
| 280 CharSequence storagePath = dataPathHelper.rewrite(path.toString()); | 282 RevlogStream content = resolveStoreFile(path); |
| 281 RevlogStream content = resolve(Path.create(storagePath), false); | |
| 282 // XXX no content when no file? or HgDataFile.exists() to detect that? | |
| 283 if (content == null) { | 283 if (content == null) { |
| 284 return new HgDataFile(this, path); | 284 return new HgDataFile(this, path); |
| 285 } | 285 } |
| 286 return new HgDataFile(this, path, content); | 286 return new HgDataFile(this, path, content); |
| 287 } | 287 } |
| 343 return new HgRepoConfig(new ConfigFile(getSessionContext())); // empty config, do not cache, allow to try once again | 343 return new HgRepoConfig(new ConfigFile(getSessionContext())); // empty config, do not cache, allow to try once again |
| 344 //throw new HgInvalidControlFileException(m, ex, null); | 344 //throw new HgInvalidControlFileException(m, ex, null); |
| 345 } | 345 } |
| 346 } | 346 } |
| 347 return repoConfig; | 347 return repoConfig; |
| 348 } | |
| 349 | |
| 350 // shall be of use only for internal classes | |
| 351 /*package-local*/ File getRepositoryRoot() { | |
| 352 return repoDir; | |
| 353 } | |
| 354 | |
| 355 /*package-local, debug*/String getStoragePath(HgDataFile df) { | |
| 356 // may come handy for debug | |
| 357 return dataPathHelper.rewrite(df.getPath().toString()).toString(); | |
| 358 } | 348 } |
| 359 | 349 |
| 360 // 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) | 350 // 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) |
| 361 // XXX consider passing Path pool or factory to produce (shared) Path instead of Strings | 351 // XXX consider passing Path pool or factory to produce (shared) Path instead of Strings |
| 362 /*package-local*/ final HgDirstate loadDirstate(Path.Source pathFactory) throws HgInvalidControlFileException { | 352 /*package-local*/ final HgDirstate loadDirstate(Path.Source pathFactory) throws HgInvalidControlFileException { |
| 457 | 447 |
| 458 @Experimental(reason="WORK IN PROGRESS") | 448 @Experimental(reason="WORK IN PROGRESS") |
| 459 public HgRepositoryLock getStoreLock() { | 449 public HgRepositoryLock getStoreLock() { |
| 460 if (storeLock == null) { | 450 if (storeLock == null) { |
| 461 int timeout = getLockTimeout(); | 451 int timeout = getLockTimeout(); |
| 462 File fl = impl.getFileFromRepoDir(repoPathHelper.rewrite("lock").toString()); | 452 File fl = impl.getFileFromStoreDir("lock"); |
| 463 synchronized (this) { | 453 synchronized (this) { |
| 464 if (storeLock == null) { | 454 if (storeLock == null) { |
| 465 storeLock = new HgRepositoryLock(fl, timeout); | 455 storeLock = new HgRepositoryLock(fl, timeout); |
| 466 } | 456 } |
| 467 } | 457 } |
| 489 return sessionContext; | 479 return sessionContext; |
| 490 } | 480 } |
| 491 | 481 |
| 492 /** | 482 /** |
| 493 * Perhaps, should be separate interface, like ContentLookup | 483 * Perhaps, should be separate interface, like ContentLookup |
| 494 * path - repository storage path (i.e. one usually with .i or .d) | 484 * @param path - normalized file name |
| 495 */ | 485 * @return <code>null</code> if path doesn't resolve to a existing file |
| 496 /*package-local*/ RevlogStream resolve(Path path, boolean shallFakeNonExistent) { | 486 */ |
| 487 /*package-local*/ RevlogStream resolveStoreFile(Path path) { | |
| 497 final SoftReference<RevlogStream> ref = streamsCache.get(path); | 488 final SoftReference<RevlogStream> ref = streamsCache.get(path); |
| 498 RevlogStream cached = ref == null ? null : ref.get(); | 489 RevlogStream cached = ref == null ? null : ref.get(); |
| 499 if (cached != null) { | 490 if (cached != null) { |
| 500 return cached; | 491 return cached; |
| 501 } | 492 } |
| 502 File f = new File(repoDir, path.toString()); | 493 File f = impl.getFileFromDataDir(path); |
| 503 if (f.exists()) { | 494 if (f.exists()) { |
| 504 RevlogStream s = new RevlogStream(impl.getDataAccess(), f); | 495 RevlogStream s = new RevlogStream(impl.getDataAccess(), f); |
| 505 if (impl.shallCacheRevlogs()) { | 496 if (impl.shallCacheRevlogs()) { |
| 506 streamsCache.put(path, new SoftReference<RevlogStream>(s)); | 497 streamsCache.put(path, new SoftReference<RevlogStream>(s)); |
| 507 } | 498 } |
| 508 return s; | 499 return s; |
| 509 } else { | 500 } |
| 510 if (shallFakeNonExistent) { | 501 return null; |
| 511 try { | 502 } |
| 512 File fake = File.createTempFile(f.getName(), null); | 503 |
| 513 fake.deleteOnExit(); | 504 private File fakeNonExistentFile(File expected) throws HgInvalidFileException { |
| 514 return new RevlogStream(impl.getDataAccess(), fake); | 505 try { |
| 515 } catch (IOException ex) { | 506 File fake = File.createTempFile(expected.getName(), null); |
| 516 getSessionContext().getLog().dump(getClass(), Info, ex, null); | 507 fake.deleteOnExit(); |
| 517 } | 508 return fake; |
| 518 } | 509 } catch (IOException ex) { |
| 519 } | 510 getSessionContext().getLog().dump(getClass(), Info, ex, null); |
| 520 return null; // XXX empty stream instead? | 511 throw new HgInvalidFileException(String.format("Failed to fake existence of file %s", expected), ex); |
| 512 } | |
| 521 } | 513 } |
| 522 | 514 |
| 523 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) { | 515 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) { |
| 524 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); | 516 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo)); |
| 525 } | 517 } |
