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
;
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/",
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()) {
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();
66 public void create() throws IOException
{
67 if (gitDir
.exists()) {
68 throw new IllegalStateException("Repository already exists: "
75 new File(objectsDir
, "pack").mkdir();
76 new File(objectsDir
, "info").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");
88 initializeWindowCache();
91 private void initializeWindowCache() {
92 // FIXME these should be configurable...
93 windows
= new WindowCache(256 * 1024 * 1024, 4);
96 public File
getDirectory() {
100 public File
getObjectsDirectory() {
104 public RepositoryConfig
getConfig() {
108 public WindowCache
getWindowCache() {
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
;
120 final byte[] tmp
= new byte[Constants
.OBJECT_ID_LENGTH
];
123 if (packs
[--k
].hasObject(objectId
, tmp
))
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.
133 return toFile(objectId
).isFile();
136 public ObjectLoader
openObject(final ObjectId id
) throws IOException
{
137 int k
= packs
.length
;
139 final byte[] tmp
= new byte[Constants
.OBJECT_ID_LENGTH
];
142 final ObjectLoader ol
= packs
[--k
].get(id
, tmp
);
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.
154 return new UnpackedObjectLoader(this, id
);
155 } catch (FileNotFoundException fnfe
) {
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
);
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
);
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
{
208 if (ObjectId
.isId(revstr
)) {
209 id
= new ObjectId(revstr
);
213 final Ref r
= readRef(revstr
, false);
215 id
= r
.getObjectId();
222 public void close() throws IOException
{
226 public void closePacks() throws IOException
{
227 for (int k
= packs
.length
- 1; k
>= 0; k
--) {
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")) {
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()
247 final ArrayList p
= new ArrayList(list
.length
);
248 for (int k
= 0; k
< list
.length
; k
++) {
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()];
261 private void writeSymref(final String name
, final String target
)
263 final File s
= new File(gitDir
, name
);
264 final File t
= File
.createTempFile("srf", null, gitDir
);
265 FileWriter w
= new FileWriter(t
);
272 if (!t
.renameTo(s
)) {
273 s
.getParentFile().mkdirs();
274 if (!t
.renameTo(s
)) {
276 throw new ObjectWritingException("Unable to"
277 + " write symref " + name
+ " to point to "
289 private Ref
readRef(final String revstr
, final boolean missingOk
)
291 for (int k
= 0; k
< refSearchPaths
.length
; k
++) {
292 final Ref r
= readRefBasic(refSearchPaths
[k
] + revstr
);
293 if (missingOk
|| r
.getObjectId() != null) {
300 private Ref
readRefBasic(String name
) throws IOException
{
303 final File f
= new File(getDirectory(), name
);
305 return new Ref(f
, null);
308 final BufferedReader br
= new BufferedReader(new FileReader(f
));
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
);
323 } while (depth
++ < 5);
324 throw new IOException("Exceed maximum ref depth. Circular reference?");
327 public String
toString() {
328 return "Repository[" + getDirectory() + "]";