Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/DirstateReader.java @ 526:2f9ed6bcefa2
Initial support for Revert command with accompanying minor refactoring
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 15 Jan 2013 17:07:19 +0100 |
parents | src/org/tmatesoft/hg/repo/HgDirstate.java@0be5be8d57e9 |
children | 47b7bedf0569 |
comparison
equal
deleted
inserted
replaced
525:0be5be8d57e9 | 526:2f9ed6bcefa2 |
---|---|
1 /* | |
2 * Copyright (c) 2010-2013 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.internal; | |
18 | |
19 import static org.tmatesoft.hg.core.Nodeid.NULL; | |
20 import static org.tmatesoft.hg.repo.HgRepositoryFiles.Dirstate; | |
21 import static org.tmatesoft.hg.util.LogFacility.Severity.Debug; | |
22 | |
23 import java.io.BufferedReader; | |
24 import java.io.File; | |
25 import java.io.FileNotFoundException; | |
26 import java.io.FileReader; | |
27 import java.io.IOException; | |
28 | |
29 import org.tmatesoft.hg.core.Nodeid; | |
30 import org.tmatesoft.hg.repo.HgDirstate; | |
31 import org.tmatesoft.hg.repo.HgDirstate.EntryKind; | |
32 import org.tmatesoft.hg.repo.HgInvalidControlFileException; | |
33 import org.tmatesoft.hg.repo.HgRepository; | |
34 import org.tmatesoft.hg.util.LogFacility.Severity; | |
35 import org.tmatesoft.hg.util.Pair; | |
36 import org.tmatesoft.hg.util.Path; | |
37 | |
38 | |
39 /** | |
40 * Parse dirstate file | |
41 * | |
42 * @see http://mercurial.selenic.com/wiki/DirState | |
43 * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate | |
44 * | |
45 * @author Artem Tikhomirov | |
46 * @author TMate Software Ltd. | |
47 */ | |
48 public final class DirstateReader { | |
49 // dirstate read code originally lived in org.tmatesoft.hg.repo.HgDirstate | |
50 | |
51 private final Internals repo; | |
52 private final Path.Source pathPool; | |
53 private Pair<Nodeid, Nodeid> parents; | |
54 | |
55 public DirstateReader(Internals hgRepo, Path.Source pathSource) { | |
56 repo = hgRepo; | |
57 pathPool = pathSource; | |
58 } | |
59 | |
60 public void readInto(HgDirstate.Inspector target) throws HgInvalidControlFileException { | |
61 EncodingHelper encodingHelper = repo.buildFileNameEncodingHelper(); | |
62 parents = new Pair<Nodeid,Nodeid>(Nodeid.NULL, Nodeid.NULL); | |
63 File dirstateFile = getDirstateFile(repo); | |
64 if (dirstateFile == null || !dirstateFile.exists()) { | |
65 return; | |
66 } | |
67 DataAccess da = repo.getDataAccess().create(dirstateFile); | |
68 try { | |
69 if (da.isEmpty()) { | |
70 return; | |
71 } | |
72 parents = internalReadParents(da); | |
73 // hg init; hg up produces an empty repository where dirstate has parents (40 bytes) only | |
74 while (!da.isEmpty()) { | |
75 final byte state = da.readByte(); | |
76 final int fmode = da.readInt(); | |
77 final int size = da.readInt(); | |
78 final int time = da.readInt(); | |
79 final int nameLen = da.readInt(); | |
80 String fn1 = null, fn2 = null; | |
81 byte[] name = new byte[nameLen]; | |
82 da.readBytes(name, 0, nameLen); | |
83 for (int i = 0; i < nameLen; i++) { | |
84 if (name[i] == 0) { | |
85 fn1 = encodingHelper.fromDirstate(name, 0, i); | |
86 fn2 = encodingHelper.fromDirstate(name, i+1, nameLen - i - 1); | |
87 break; | |
88 } | |
89 } | |
90 if (fn1 == null) { | |
91 fn1 = encodingHelper.fromDirstate(name, 0, nameLen); | |
92 } | |
93 HgDirstate.Record r = new HgDirstate.Record(fmode, size, time, pathPool.path(fn1), fn2 == null ? null : pathPool.path(fn2)); | |
94 if (state == 'n') { | |
95 target.next(EntryKind.Normal, r); | |
96 } else if (state == 'a') { | |
97 target.next(EntryKind.Added, r); | |
98 } else if (state == 'r') { | |
99 target.next(EntryKind.Removed, r); | |
100 } else if (state == 'm') { | |
101 target.next(EntryKind.Merged, r); | |
102 } else { | |
103 repo.getSessionContext().getLog().dump(getClass(), Severity.Warn, "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name(), r.size(), r.modificationTime(), state); | |
104 } | |
105 } | |
106 } catch (IOException ex) { | |
107 throw new HgInvalidControlFileException("Dirstate read failed", ex, dirstateFile); | |
108 } finally { | |
109 da.done(); | |
110 } | |
111 } | |
112 | |
113 private static Pair<Nodeid, Nodeid> internalReadParents(DataAccess da) throws IOException { | |
114 byte[] parents = new byte[40]; | |
115 da.readBytes(parents, 0, 40); | |
116 Nodeid n1 = Nodeid.fromBinary(parents, 0); | |
117 Nodeid n2 = Nodeid.fromBinary(parents, 20); | |
118 parents = null; | |
119 return new Pair<Nodeid, Nodeid>(n1, n2); | |
120 } | |
121 | |
122 /** | |
123 * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values. | |
124 */ | |
125 public Pair<Nodeid,Nodeid> parents() { | |
126 assert parents != null; // instance not initialized with #read() | |
127 return parents; | |
128 } | |
129 | |
130 private static File getDirstateFile(Internals repo) { | |
131 return repo.getFileFromRepoDir(Dirstate.getName()); | |
132 } | |
133 | |
134 /** | |
135 * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available | |
136 */ | |
137 public static Pair<Nodeid, Nodeid> readParents(Internals internalRepo) throws HgInvalidControlFileException { | |
138 // do not read whole dirstate if all we need is WC parent information | |
139 File dirstateFile = getDirstateFile(internalRepo); | |
140 if (dirstateFile == null || !dirstateFile.exists()) { | |
141 return new Pair<Nodeid,Nodeid>(NULL, NULL); | |
142 } | |
143 DataAccess da = internalRepo.getDataAccess().create(dirstateFile); | |
144 try { | |
145 if (da.isEmpty()) { | |
146 return new Pair<Nodeid,Nodeid>(NULL, NULL); | |
147 } | |
148 return internalReadParents(da); | |
149 } catch (IOException ex) { | |
150 throw new HgInvalidControlFileException("Error reading working copy parents from dirstate", ex, dirstateFile); | |
151 } finally { | |
152 da.done(); | |
153 } | |
154 } | |
155 | |
156 /** | |
157 * TODO [post-1.0] it's really not a proper place for the method, need WorkingCopyContainer or similar | |
158 * @return branch associated with the working directory | |
159 */ | |
160 public static String readBranch(Internals internalRepo) throws HgInvalidControlFileException { | |
161 File branchFile = internalRepo.getFileFromRepoDir("branch"); // FIXME constant in the HgRepositoryFiles | |
162 String branch = HgRepository.DEFAULT_BRANCH_NAME; | |
163 if (branchFile.exists()) { | |
164 try { | |
165 BufferedReader r = new BufferedReader(new FileReader(branchFile)); | |
166 String b = r.readLine(); | |
167 if (b != null) { | |
168 b = b.trim().intern(); | |
169 } | |
170 branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; | |
171 r.close(); | |
172 } catch (FileNotFoundException ex) { | |
173 internalRepo.getSessionContext().getLog().dump(HgDirstate.class, Debug, ex, null); // log verbose debug, exception might be legal here | |
174 // IGNORE | |
175 } catch (IOException ex) { | |
176 throw new HgInvalidControlFileException("Error reading file with branch information", ex, branchFile); | |
177 } | |
178 } | |
179 return branch; | |
180 } | |
181 } |