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 }