1 /*
2 * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
3 * Copyright (C) 2009-2010, Google Inc.
4 * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Distribution License v. 1.0 which is available at
9 * https://www.eclipse.org/org/documents/edl-v10.php.
10 *
11 * SPDX-License-Identifier: BSD-3-Clause
12 */
13
14 package org.eclipse.jgit.lib;
15
16 import java.io.IOException;
17 import java.io.StringWriter;
18 import java.util.Collection;
19 import java.util.Map;
20
21 import org.eclipse.jgit.internal.storage.file.RefDirectory;
22 import org.eclipse.jgit.util.RefList;
23 import org.eclipse.jgit.util.RefMap;
24
25 /**
26 * Writes out refs to the {@link org.eclipse.jgit.lib.Constants#INFO_REFS} and
27 * {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} files.
28 *
29 * This class is abstract as the writing of the files must be handled by the
30 * caller. This is because it is used by transport classes as well.
31 */
32 public abstract class RefWriter {
33
34 private final Collection<Ref> refs;
35
36 /**
37 * <p>Constructor for RefWriter.</p>
38 *
39 * @param refs
40 * the complete set of references. This should have been computed
41 * by applying updates to the advertised refs already discovered.
42 */
43 public RefWriter(Collection<Ref> refs) {
44 this.refs = RefComparator.sort(refs);
45 }
46
47 /**
48 * <p>Constructor for RefWriter.</p>
49 *
50 * @param refs
51 * the complete set of references. This should have been computed
52 * by applying updates to the advertised refs already discovered.
53 */
54 public RefWriter(Map<String, Ref> refs) {
55 if (refs instanceof RefMap)
56 this.refs = refs.values();
57 else
58 this.refs = RefComparator.sort(refs.values());
59 }
60
61 /**
62 * <p>Constructor for RefWriter.</p>
63 *
64 * @param refs
65 * the complete set of references. This should have been computed
66 * by applying updates to the advertised refs already discovered.
67 */
68 public RefWriter(RefList<Ref> refs) {
69 this.refs = refs.asList();
70 }
71
72 /**
73 * Rebuild the {@link org.eclipse.jgit.lib.Constants#INFO_REFS}.
74 * <p>
75 * This method rebuilds the contents of the
76 * {@link org.eclipse.jgit.lib.Constants#INFO_REFS} file to match the passed
77 * list of references.
78 *
79 * @throws java.io.IOException
80 * writing is not supported, or attempting to write the file
81 * failed, possibly due to permissions or remote disk full, etc.
82 */
83 public void writeInfoRefs() throws IOException {
84 final StringWriter w = new StringWriter();
85 final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
86 for (Ref r : refs) {
87 if (Constants.HEAD.equals(r.getName())) {
88 // Historically HEAD has never been published through
89 // the INFO_REFS file. This is a mistake, but its the
90 // way things are.
91 //
92 continue;
93 }
94
95 ObjectId objectId = r.getObjectId();
96 if (objectId == null) {
97 // Symrefs to unborn branches aren't advertised in the info/refs
98 // file.
99 continue;
100 }
101 objectId.copyTo(tmp, w);
102 w.write('\t');
103 w.write(r.getName());
104 w.write('\n');
105
106 ObjectId peeledObjectId = r.getPeeledObjectId();
107 if (peeledObjectId != null) {
108 peeledObjectId.copyTo(tmp, w);
109 w.write('\t');
110 w.write(r.getName());
111 w.write("^{}\n"); //$NON-NLS-1$
112 }
113 }
114 writeFile(Constants.INFO_REFS, Constants.encode(w.toString()));
115 }
116
117 /**
118 * Rebuild the {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} file.
119 * <p>
120 * This method rebuilds the contents of the
121 * {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} file to match the
122 * passed list of references, including only those refs that have a storage
123 * type of {@link org.eclipse.jgit.lib.Ref.Storage#PACKED}.
124 *
125 * @throws java.io.IOException
126 * writing is not supported, or attempting to write the file
127 * failed, possibly due to permissions or remote disk full, etc.
128 */
129 public void writePackedRefs() throws IOException {
130 boolean peeled = false;
131 for (Ref r : refs) {
132 if (r.getStorage().isPacked() && r.isPeeled()) {
133 peeled = true;
134 break;
135 }
136 }
137
138 final StringWriter w = new StringWriter();
139 if (peeled) {
140 w.write(RefDirectory.PACKED_REFS_HEADER);
141 if (peeled)
142 w.write(RefDirectory.PACKED_REFS_PEELED);
143 w.write('\n');
144 }
145
146 final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
147 for (Ref r : refs) {
148 if (r.getStorage() != Ref.Storage.PACKED)
149 continue;
150
151 ObjectId objectId = r.getObjectId();
152 if (objectId == null) {
153 // A packed ref cannot be a symref, let alone a symref
154 // to an unborn branch.
155 throw new NullPointerException();
156 }
157 objectId.copyTo(tmp, w);
158 w.write(' ');
159 w.write(r.getName());
160 w.write('\n');
161
162 ObjectId peeledObjectId = r.getPeeledObjectId();
163 if (peeledObjectId != null) {
164 w.write('^');
165 peeledObjectId.copyTo(tmp, w);
166 w.write('\n');
167 }
168 }
169 writeFile(Constants.PACKED_REFS, Constants.encode(w.toString()));
170 }
171
172 /**
173 * Handles actual writing of ref files to the git repository, which may
174 * differ slightly depending on the destination and transport.
175 *
176 * @param file
177 * path to ref file.
178 * @param content
179 * byte content of file to be written.
180 * @throws java.io.IOException
181 */
182 protected abstract void writeFile(String file, byte[] content)
183 throws IOException;
184 }