summaryrefslogtreecommitdiff
path: root/refs/packed-backend.h
blob: 1936bb5c76c14178e85f88b485b112b5df9e2a7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#ifndef REFS_PACKED_BACKEND_H
#define REFS_PACKED_BACKEND_H

#include "../cache.h"
#include "refs-internal.h"
#include "../lockfile.h"

struct repository;
struct ref_transaction;

/*
 * Support for storing references in a `packed-refs` file.
 *
 * Note that this backend doesn't check for D/F conflicts, because it
 * doesn't care about them. But usually it should be wrapped in a
 * `files_ref_store` that prevents D/F conflicts from being created,
 * even among packed refs.
 */

struct ref_store *packed_ref_store_create(struct repository *repo,
					  const char *gitdir,
					  unsigned int store_flags);

/*
 * Lock the packed-refs file for writing. Flags is passed to
 * hold_lock_file_for_update(). Return 0 on success. On errors, write
 * an error message to `err` and return a nonzero value.
 */
int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err);

void packed_refs_unlock(struct ref_store *ref_store);
int packed_refs_is_locked(struct ref_store *ref_store);

/*
 * Return true if `transaction` really needs to be carried out against
 * the specified packed_ref_store, or false if it can be skipped
 * (i.e., because it is an obvious NOOP). `ref_store` must be locked
 * before calling this function.
 */
int is_packed_transaction_needed(struct ref_store *ref_store,
				 struct ref_transaction *transaction);

struct packed_ref_store;

/*
 * A `snapshot` represents one snapshot of a `packed-refs` file.
 *
 * Normally, this will be a mmapped view of the contents of the
 * `packed-refs` file at the time the snapshot was created. However,
 * if the `packed-refs` file was not sorted, this might point at heap
 * memory holding the contents of the `packed-refs` file with its
 * records sorted by refname.
 *
 * `snapshot` instances are reference counted (via
 * `acquire_snapshot()` and `release_snapshot()`). This is to prevent
 * an instance from disappearing while an iterator is still iterating
 * over it. Instances are garbage collected when their `referrers`
 * count goes to zero.
 *
 * The most recent `snapshot`, if available, is referenced by the
 * `packed_ref_store`. Its freshness is checked whenever
 * `get_snapshot()` is called; if the existing snapshot is obsolete, a
 * new snapshot is taken.
 */
struct snapshot {
	/*
	 * A back-pointer to the packed_ref_store with which this
	 * snapshot is associated:
	 */
	struct packed_ref_store *refs;

	/* Is the `packed-refs` file currently mmapped? */
	int mmapped;

	/* which file format version is this file? */
	int version;

	/*
	 * The contents of the `packed-refs` file:
	 *
	 * - buf -- a pointer to the start of the memory
	 * - start -- a pointer to the first byte of actual references
	 *   (i.e., after the header line, if one is present)
	 * - eof -- a pointer just past the end of the reference
	 *   contents
	 *
	 * If the `packed-refs` file was already sorted, `buf` points
	 * at the mmapped contents of the file. If not, it points at
	 * heap-allocated memory containing the contents, sorted. If
	 * there were no contents (e.g., because the file didn't
	 * exist), `buf`, `start`, and `eof` are all NULL.
	 */
	char *buf, *start, *eof;

	/*
	 * What is the peeled state of the `packed-refs` file that
	 * this snapshot represents? (This is usually determined from
	 * the file's header.)
	 */
	enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled;

	/*************************
	 * packed-refs v2 values *
	 *************************/
	size_t nr;
	size_t prefixes_nr;
	size_t buflen;
	const unsigned char *offset_chunk;
	const char *refs_chunk;
	const unsigned char *prefix_offsets_chunk;
	const char *prefix_chunk;

	/*
	 * Count of references to this instance, including the pointer
	 * from `packed_ref_store::snapshot`, if any. The instance
	 * will not be freed as long as the reference count is
	 * nonzero.
	 */
	unsigned int referrers;

	/*
	 * The metadata of the `packed-refs` file from which this
	 * snapshot was created, used to tell if the file has been
	 * replaced since we read it.
	 */
	struct stat_validity validity;
};

int release_snapshot(struct snapshot *snapshot);

/*
 * If the buffer in `snapshot` is active, then either munmap the
 * memory and close the file, or free the memory. Then set the buffer
 * pointers to NULL.
 */
void clear_snapshot_buffer(struct snapshot *snapshot);

/*
 * A `ref_store` representing references stored in a `packed-refs`
 * file. It implements the `ref_store` interface, though it has some
 * limitations:
 *
 * - It cannot store symbolic references.
 *
 * - It cannot store reflogs.
 *
 * - It does not support reference renaming (though it could).
 *
 * On the other hand, it can be locked outside of a reference
 * transaction. In that case, it remains locked even after the
 * transaction is done and the new `packed-refs` file is activated.
 */
struct packed_ref_store {
	struct ref_store base;

	unsigned int store_flags;

	/* The path of the "packed-refs" file: */
	char *path;

	/*
	 * A snapshot of the values read from the `packed-refs` file,
	 * if it might still be current; otherwise, NULL.
	 */
	struct snapshot *snapshot;

	/*
	 * Lock used for the "packed-refs" file. Note that this (and
	 * thus the enclosing `packed_ref_store`) must not be freed.
	 */
	struct lock_file lock;

	/*
	 * Temporary file used when rewriting new contents to the
	 * "packed-refs" file. Note that this (and thus the enclosing
	 * `packed_ref_store`) must not be freed.
	 */
	struct tempfile *tempfile;
};

/*
 * This value is set in `base.flags` if the peeled value of the
 * current reference is known. In that case, `peeled` contains the
 * correct peeled value for the reference, which might be `null_oid`
 * if the reference is not a tag or if it is broken.
 */
#define REF_KNOWS_PEELED 0x40

/*
 * An iterator over a snapshot of a `packed-refs` file.
 */
struct packed_ref_iterator {
	struct ref_iterator base;
	struct snapshot *snapshot;
	struct repository *repo;
	unsigned int flags;
	int version;

	/* Scratch space for current values: */
	struct object_id oid, peeled;
	struct strbuf refname_buf;

	/* The current position in the snapshot's buffer: */
	const char *pos;

	/***********************************
	 * packed-refs v1 iterator values. *
	 ***********************************/

	/* The end of the part of the buffer that will be iterated over: */
	const char *eof;

	/***********************************
	 * packed-refs v2 iterator values. *
	 ***********************************/
	size_t nr;
	size_t row;
	size_t prefix_row_end;
	size_t prefix_i;
	const char *cur_prefix;
};

typedef int (*write_ref_fn)(const char *refname,
			    const struct object_id *oid,
			    const struct object_id *peeled,
			    void *write_data);

int merge_iterator_and_updates(struct packed_ref_store *refs,
			       struct string_list *updates,
			       struct strbuf *err,
			       write_ref_fn write_fn,
			       void *write_data);

/**
 * Parse the buffer at the given snapshot to verify that it is a
 * packed-refs file in version 1 format. Update the snapshot->peeled
 * value according to the header information. Update the given
 * 'sorted' value with whether or not the packed-refs file is sorted.
 */
int parse_packed_format_v1_header(struct packed_ref_store *refs,
				  struct snapshot *snapshot,
				  int *sorted);

/*
 * Find the place in `snapshot->buf` where the start of the record for
 * `refname` starts. If `mustexist` is true and the reference doesn't
 * exist, then return NULL. If `mustexist` is false and the reference
 * doesn't exist, then return the point where that reference would be
 * inserted, or `snapshot->eof` (which might be NULL) if it would be
 * inserted at the end of the file. In the latter mode, `refname`
 * doesn't have to be a proper reference name; for example, one could
 * search for "refs/replace/" to find the start of any replace
 * references.
 *
 * The record is sought using a binary search, so `snapshot->buf` must
 * be sorted.
 */
const char *find_reference_location_v1(struct snapshot *snapshot,
				       const char *refname, int mustexist);

int packed_read_raw_ref_v1(struct packed_ref_store *refs, struct snapshot *snapshot,
			   const char *refname, struct object_id *oid,
			   unsigned int *type, int *failure_errno);

void verify_buffer_safe_v1(struct snapshot *snapshot);
void sort_snapshot_v1(struct snapshot *snapshot);
int write_packed_file_header_v1(FILE *out);
int next_record_v1(struct packed_ref_iterator *iter);
int write_packed_entry_v1(const char *refname,
			  const struct object_id *oid,
			  const struct object_id *peeled,
			  void *write_data);

/**
 * Parse the buffer at the given snapshot to verify that it is a
 * packed-refs file in version 1 format. Update the snapshot->peeled
 * value according to the header information. Update the given
 * 'sorted' value with whether or not the packed-refs file is sorted.
 */
int parse_packed_format_v1_header(struct packed_ref_store *refs,
				  struct snapshot *snapshot,
				  int *sorted);

int detect_packed_format_v2_header(struct packed_ref_store *refs,
				   struct snapshot *snapshot);
/*
 * Find the place in `snapshot->buf` where the start of the record for
 * `refname` starts. If `mustexist` is true and the reference doesn't
 * exist, then return NULL. If `mustexist` is false and the reference
 * doesn't exist, then return the point where that reference would be
 * inserted, or `snapshot->eof` (which might be NULL) if it would be
 * inserted at the end of the file. In the latter mode, `refname`
 * doesn't have to be a proper reference name; for example, one could
 * search for "refs/replace/" to find the start of any replace
 * references.
 *
 * The record is sought using a binary search, so `snapshot->buf` must
 * be sorted.
 */
const char *find_reference_location_v2(struct snapshot *snapshot,
				       const char *refname, int mustexist,
				       size_t *pos);

int packed_read_raw_ref_v2(struct packed_ref_store *refs, struct snapshot *snapshot,
			   const char *refname, struct object_id *oid,
			   unsigned int *type, int *failure_errno);
int next_record_v2(struct packed_ref_iterator *iter);
void fill_snapshot_v2(struct snapshot *snapshot);

struct write_packed_refs_v2_context;
struct write_packed_refs_v2_context *create_v2_context(struct packed_ref_store *refs,
						       struct string_list *updates,
						       struct strbuf *err);
int write_packed_refs_v2(struct write_packed_refs_v2_context *ctx);
void free_v2_context(struct write_packed_refs_v2_context *ctx);

void init_iterator_prefix_info(const char *prefix,
			       struct packed_ref_iterator *iter);

#endif /* REFS_PACKED_BACKEND_H */