Don't write a tree unless any symbolic links referenced
[egit/egit-new.git] / src / org / spearce / jgit / lib / WriteTree.java
blob1de0b8919ea8f137c93ebafd0bef07f119e2250c
1 package org.spearce.jgit.lib;
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.UnsupportedEncodingException;
11 import java.security.MessageDigest;
12 import java.security.NoSuchAlgorithmException;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.Iterator;
16 import java.util.zip.DeflaterOutputStream;
18 public class WriteTree extends TreeVisitor {
19 private static final TreeNameComparator TNC = new TreeNameComparator();
21 private static final byte[] TREE_MODE;
23 private static final byte[] SYMLINK_MODE;
25 private static final byte[] PLAIN_FILE_MODE;
27 private static final byte[] EXECUTABLE_FILE_MODE;
29 static {
30 try {
31 TREE_MODE = "40000".getBytes("UTF-8");
32 SYMLINK_MODE = "120000".getBytes("UTF-8");
33 PLAIN_FILE_MODE = "100644".getBytes("UTF-8");
34 EXECUTABLE_FILE_MODE = "100755".getBytes("UTF-8");
35 } catch (UnsupportedEncodingException uue) {
36 throw new ExceptionInInitializerError(uue);
40 private final ObjectDatabase objdb;
42 private final byte[] copybuf;
44 private final MessageDigest md;
46 private final boolean writeAll;
48 private File src;
50 public WriteTree(final ObjectDatabase db, final File sourceDir)
51 throws NoSuchAlgorithmException {
52 objdb = db;
53 copybuf = new byte[8192];
54 md = MessageDigest.getInstance("SHA-1");
55 writeAll = true;
56 src = sourceDir;
59 protected void visitFile(final FileTreeEntry f) throws IOException {
60 super.visitFile(f);
61 final File d = new File(src, f.getName());
62 final FileInputStream is = new FileInputStream(d);
63 try {
64 f.setId(writeObject("blob", (int) d.length(), is));
65 } finally {
66 is.close();
70 protected void visitSymlink(final SymlinkTreeEntry s) throws IOException {
71 super.visitSymlink(s);
73 if (!objdb.checkObject(s.getId())) {
74 throw new CorruptObjectException("Missing symlink blob "
75 + s.getId());
79 protected void visitTree(final Tree t) throws IOException {
80 // Only visit a tree if it has been modified. If any child of a tree has
81 // been modified then the tree itself will also appear modified;
82 // consequently we will recurse into it.
84 if (writeAll || t.isModified()) {
85 if (t.isRoot()) {
86 super.visitTree(t);
87 } else {
88 final File d = new File(src, t.getName());
89 final File o = src;
90 src = d;
91 super.visitTree(t);
92 src = o;
95 final ByteArrayOutputStream o = new ByteArrayOutputStream();
96 final ArrayList r = new ArrayList();
97 final byte[] d;
98 Iterator i;
100 i = t.entryIterator();
101 while (i.hasNext()) {
102 r.add(i.next());
104 Collections.sort(r, TNC);
106 i = r.iterator();
107 while (i.hasNext()) {
108 final TreeEntry e = (TreeEntry) i.next();
109 final byte[] mode;
111 if (e instanceof Tree) {
112 mode = TREE_MODE;
113 } else if (e instanceof SymlinkTreeEntry) {
114 mode = SYMLINK_MODE;
115 } else if (e instanceof FileTreeEntry) {
116 mode = ((FileTreeEntry) e).isExecutable() ? EXECUTABLE_FILE_MODE
117 : PLAIN_FILE_MODE;
118 } else {
119 throw new IOException("Object not supported in Tree:" + e);
122 o.write(mode);
123 o.write(' ');
124 o.write(e.getNameUTF8());
125 o.write(0);
126 o.write(e.getId().getBytes());
128 d = o.toByteArray();
129 t.setId(writeObject("tree", d.length, new ByteArrayInputStream(d)));
133 private ObjectId writeObject(final String type, int len,
134 final InputStream is) throws IOException {
135 final File t = File.createTempFile("noz", null, objdb
136 .getObjectsDirectory());
137 final DeflaterOutputStream ts = new DeflaterOutputStream(
138 new FileOutputStream(t));
139 ObjectId id = null;
140 try {
141 final byte[] header = (type + " " + len + "\0").getBytes("UTF-8");
142 int r;
144 md.update(header);
145 ts.write(header);
147 while ((r = is.read(copybuf)) > 0 && len > 0) {
148 if (r > len) {
149 r = len;
151 md.update(copybuf, 0, r);
152 ts.write(copybuf, 0, r);
153 len -= r;
155 if (len != 0) {
156 throw new CorruptObjectException("Input was short: " + len);
159 ts.close();
160 id = new ObjectId(md.digest());
161 } finally {
162 if (id == null) {
163 md.reset();
164 ts.close();
165 t.delete();
169 if (objdb.checkObject(id)) {
170 // Object is already in the repository so remove the temporary file.
172 t.delete();
173 } else {
174 final File o = objdb.objectFile(id);
175 o.getParentFile().mkdir();
176 if (!t.renameTo(o)) {
177 if (!objdb.checkObject(id)) {
178 // The object failed to be renamed into its proper location
179 // and it doesn't exist in the repository either. (The
180 // rename could have failed if another process was placing
181 // the object there at the same time as us.) We really don't
182 // know what went wrong, so abort.
184 t.delete();
185 throw new IOException("Failed to write object " + id);
190 return id;