Reduce the number of temporary byte[20] arrays allocated.
[egit/egit-new.git] / org.spearce.jgit / src / org / spearce / jgit / lib / Repository.java
blob3e91f90f696d32c2b6795181a10c15798aa49c89
1 /*
2 * Copyright (C) 2006 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License, version 2.1, as published by the Free Software Foundation.
8 * This library 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 GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.FileFilter;
22 import java.io.FileNotFoundException;
23 import java.io.FileReader;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.util.ArrayList;
28 import org.spearce.jgit.errors.IncorrectObjectTypeException;
29 import org.spearce.jgit.errors.ObjectWritingException;
31 public class Repository {
32 private static final String[] refSearchPaths = { "", "refs/", "refs/tags/",
33 "refs/heads/", };
35 private final File gitDir;
37 private final File objectsDir;
39 private final File refsDir;
41 private final RepositoryConfig config;
43 private PackFile[] packs;
45 private WindowCache windows;
47 public Repository(final File d) throws IOException {
48 gitDir = d.getAbsoluteFile();
49 objectsDir = new File(gitDir, "objects");
50 refsDir = new File(gitDir, "refs");
51 packs = new PackFile[0];
52 config = new RepositoryConfig(this);
53 if (objectsDir.exists()) {
54 getConfig().load();
55 final String repositoryFormatVersion = getConfig().getString(
56 "core", "repositoryFormatVersion");
57 if (!"0".equals(repositoryFormatVersion)) {
58 throw new IOException("Unknown repository format \""
59 + repositoryFormatVersion + "\"; expected \"0\".");
61 initializeWindowCache();
62 scanForPacks();
66 public void create() throws IOException {
67 if (gitDir.exists()) {
68 throw new IllegalStateException("Repository already exists: "
69 + gitDir);
72 gitDir.mkdirs();
74 objectsDir.mkdirs();
75 new File(objectsDir, "pack").mkdir();
76 new File(objectsDir, "info").mkdir();
78 refsDir.mkdir();
79 new File(refsDir, "heads").mkdir();
80 new File(refsDir, "tags").mkdir();
82 new File(gitDir, "branches").mkdir();
83 new File(gitDir, "remotes").mkdir();
84 writeSymref("HEAD", "refs/heads/master");
86 getConfig().create();
87 getConfig().save();
88 initializeWindowCache();
91 private void initializeWindowCache() {
92 // FIXME these should be configurable...
93 windows = new WindowCache(256 * 1024 * 1024, 4);
96 public File getDirectory() {
97 return gitDir;
100 public File getObjectsDirectory() {
101 return objectsDir;
104 public RepositoryConfig getConfig() {
105 return config;
108 public WindowCache getWindowCache() {
109 return windows;
112 public File toFile(final ObjectId objectId) {
113 final String n = objectId.toString();
114 return new File(new File(objectsDir, n.substring(0, 2)), n.substring(2));
117 public boolean hasObject(final ObjectId objectId) {
118 int k = packs.length;
119 if (k > 0) {
120 final byte[] tmp = new byte[Constants.OBJECT_ID_LENGTH];
121 do {
122 try {
123 if (packs[--k].hasObject(objectId, tmp))
124 return true;
125 } catch (IOException ioe) {
126 // This shouldn't happen unless the pack was corrupted
127 // after we opened it. We'll ignore the error as though
128 // the object does not exist in this pack.
131 } while (k > 0);
133 return toFile(objectId).isFile();
136 public ObjectLoader openObject(final ObjectId id) throws IOException {
137 int k = packs.length;
138 if (k > 0) {
139 final byte[] tmp = new byte[Constants.OBJECT_ID_LENGTH];
140 do {
141 try {
142 final ObjectLoader ol = packs[--k].get(id, tmp);
143 if (ol != null)
144 return ol;
145 } catch (IOException ioe) {
146 // This shouldn't happen unless the pack was corrupted
147 // after we opened it. We'll ignore the error as though
148 // the object does not exist in this pack.
151 } while (k > 0);
153 try {
154 return new UnpackedObjectLoader(this, id);
155 } catch (FileNotFoundException fnfe) {
156 return null;
160 public ObjectLoader openBlob(final ObjectId id) throws IOException {
161 return openObject(id);
164 public ObjectLoader openTree(final ObjectId id) throws IOException {
165 return openObject(id);
168 public Commit mapCommit(final String revstr) throws IOException {
169 final ObjectId id = resolve(revstr);
170 return id != null ? mapCommit(id) : null;
173 public Commit mapCommit(final ObjectId id) throws IOException {
174 final ObjectLoader or = openObject(id);
175 if (or == null)
176 return null;
177 final byte[] raw = or.getBytes();
178 if (Constants.TYPE_COMMIT.equals(or.getType()))
179 return new Commit(this, id, raw);
180 throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
183 public Tree mapTree(final String revstr) throws IOException {
184 final ObjectId id = resolve(revstr);
185 return id != null ? mapTree(id) : null;
188 public Tree mapTree(final ObjectId id) throws IOException {
189 final ObjectLoader or = openObject(id);
190 if (or == null)
191 return null;
192 final byte[] raw = or.getBytes();
193 if (Constants.TYPE_TREE.equals(or.getType()))
194 return new Tree(this, id, raw);
195 if (Constants.TYPE_COMMIT.equals(or.getType()))
196 return mapTree(ObjectId.fromString(raw, 5));
197 throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
200 public RefLock lockRef(final String ref) throws IOException {
201 final RefLock l = new RefLock(readRef(ref, true));
202 return l.lock() ? l : null;
205 public ObjectId resolve(final String revstr) throws IOException {
206 ObjectId id = null;
208 if (ObjectId.isId(revstr)) {
209 id = new ObjectId(revstr);
212 if (id == null) {
213 final Ref r = readRef(revstr, false);
214 if (r != null) {
215 id = r.getObjectId();
219 return id;
222 public void close() throws IOException {
223 closePacks();
226 public void closePacks() throws IOException {
227 for (int k = packs.length - 1; k >= 0; k--) {
228 packs[k].close();
230 packs = new PackFile[0];
233 public void scanForPacks() {
234 final File packDir = new File(objectsDir, "pack");
235 final File[] list = packDir.listFiles(new FileFilter() {
236 public boolean accept(final File f) {
237 final String n = f.getName();
238 if (!n.endsWith(".pack")) {
239 return false;
241 final String nBase = n.substring(0, n.lastIndexOf('.'));
242 final File idx = new File(packDir, nBase + ".idx");
243 return f.isFile() && f.canRead() && idx.isFile()
244 && idx.canRead();
247 final ArrayList p = new ArrayList(list.length);
248 for (int k = 0; k < list.length; k++) {
249 try {
250 p.add(new PackFile(this, list[k]));
251 } catch (IOException ioe) {
252 // Whoops. That's not a pack!
256 final PackFile[] arr = new PackFile[p.size()];
257 p.toArray(arr);
258 packs = arr;
261 private void writeSymref(final String name, final String target)
262 throws IOException {
263 final File s = new File(gitDir, name);
264 final File t = File.createTempFile("srf", null, gitDir);
265 FileWriter w = new FileWriter(t);
266 try {
267 w.write("ref: ");
268 w.write(target);
269 w.write('\n');
270 w.close();
271 w = null;
272 if (!t.renameTo(s)) {
273 s.getParentFile().mkdirs();
274 if (!t.renameTo(s)) {
275 t.delete();
276 throw new ObjectWritingException("Unable to"
277 + " write symref " + name + " to point to "
278 + target);
281 } finally {
282 if (w != null) {
283 w.close();
284 t.delete();
289 private Ref readRef(final String revstr, final boolean missingOk)
290 throws IOException {
291 for (int k = 0; k < refSearchPaths.length; k++) {
292 final Ref r = readRefBasic(refSearchPaths[k] + revstr);
293 if (missingOk || r.getObjectId() != null) {
294 return r;
297 return null;
300 private Ref readRefBasic(String name) throws IOException {
301 int depth = 0;
302 REF_READING: do {
303 final File f = new File(getDirectory(), name);
304 if (!f.isFile()) {
305 return new Ref(f, null);
308 final BufferedReader br = new BufferedReader(new FileReader(f));
309 try {
310 final String line = br.readLine();
311 if (line == null || line.length() == 0) {
312 return new Ref(f, null);
313 } else if (line.startsWith("ref: ")) {
314 name = line.substring("ref: ".length());
315 continue REF_READING;
316 } else if (ObjectId.isId(line)) {
317 return new Ref(f, new ObjectId(line));
319 throw new IOException("Not a ref: " + name + ": " + line);
320 } finally {
321 br.close();
323 } while (depth++ < 5);
324 throw new IOException("Exceed maximum ref depth. Circular reference?");
327 public String toString() {
328 return "Repository[" + getDirectory() + "]";