@@ -493,6 +493,8 @@ include::config/rebase.txt[]
include::config/receive.txt[]
+include::config/refs.txt[]
+
include::config/remote.txt[]
include::config/remotes.txt[]
@@ -35,17 +35,34 @@ indicate the existence of different layers:
`files`, the `packed` format will only be used to group multiple
loose object files upon request via the `git pack-refs` command or
via the `pack-refs` maintenance task.
+
+`packed-v2`;;
+ When present, references may be stored as a group in a
+ `packed-refs` file in its version 2 format. This file is in the
+ same position and interacts with loose refs the same as when the
+ `packed` value exists. Both `packed` and `packed-v2` must exist to
+ upgrade an existing `packed-refs` file from version 1 to version 2
+ or to downgrade from version 2 to version 1. When both are
+ present, the `refs.packedRefsVersion` config value indicates which
+ file format version is used during writes, but both versions are
+ understood when reading the file.
--
+
The following combinations are supported by this version of Git:
+
--
-`files` and `packed`;;
+`files` and (`packed` and/or `packed-v2`);;
This set of values indicates that references are stored both as
- loose reference files and in the `packed-refs` file in its v1
- format. Loose references are preferred, and the `packed-refs` file
- is updated only when deleting a reference that is stored in the
- `packed-refs` file or during a `git pack-refs` command.
+ loose reference files and in the `packed-refs` file. Loose
+ references are preferred, and the `packed-refs` file is updated
+ only when deleting a reference that is stored in the `packed-refs`
+ file or during a `git pack-refs` command.
++
+The presence of `packed` and `packed-v2` specifies whether the `packed-refs`
+file is allowed to be in its v1 or v2 formats, respectively. When only one
+is present, Git will refuse to read the `packed-refs` file that do not
+match the expected format. When both are present, the `refs.packedRefsVersion`
+config option indicates which file format is used during writes.
`files`;;
When only this value is present, Git will ignore the `packed-refs`
new file mode 100644
@@ -0,0 +1,13 @@
+refs.packedRefsVersion::
+ Specifies the file format version to use when writing a `packed-refs`
+ file. Defaults to `1`.
++
+The only other value currently allowed is `2`, which uses a structured file
+format to result in a smaller `packed-refs` file. In order to write this
+file format version, the repository must also have the `packed-v2` extension
+enabled. The most typical setup will include the
+`core.repositoryFormatVersion=1` config value and the `extensions.refFormat`
+key will have three values: `files`, `packed`, and `packed-v2`.
++
+If `extensions.refFormat` has the value `packed-v2` and not `packed`, then
+`refs.packedRefsVersion` defaults to `2`.
@@ -1987,6 +1987,8 @@ static int add_ref_format_flags(enum ref_format_flags flags, int caps) {
caps |= REF_STORE_FORMAT_FILES;
if (flags & REF_FORMAT_PACKED)
caps |= REF_STORE_FORMAT_PACKED;
+ if (flags & REF_FORMAT_PACKED_V2)
+ caps |= REF_STORE_FORMAT_PACKED_V2;
return caps;
}
@@ -2006,7 +2008,7 @@ static struct ref_store *ref_store_init(struct repository *repo,
flags = add_ref_format_flags(repo->ref_format, flags);
if (!(flags & REF_STORE_FORMAT_FILES) &&
- (flags & REF_STORE_FORMAT_PACKED))
+ packed_refs_enabled(flags))
be_name = "packed";
be = find_ref_storage_backend(be_name);
@@ -236,7 +236,13 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
if (!load_contents(snapshot))
return snapshot;
- if (parse_packed_format_v1_header(refs, snapshot, &sorted)) {
+ /*
+ * If this is a v1 file format, but we don't have v1 enabled,
+ * then ignore it the same way we would as if we didn't
+ * understand it.
+ */
+ if (parse_packed_format_v1_header(refs, snapshot, &sorted) ||
+ !(refs->store_flags & REF_STORE_FORMAT_PACKED)) {
clear_snapshot(refs);
return NULL;
}
@@ -310,6 +316,12 @@ static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname,
packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
struct snapshot *snapshot = get_snapshot(refs);
+ if (!snapshot) {
+ /* refname is not a packed reference. */
+ *failure_errno = ENOENT;
+ return -1;
+ }
+
return packed_read_raw_ref_v1(refs, snapshot, refname,
oid, type, failure_errno);
}
@@ -410,6 +422,9 @@ static struct ref_iterator *packed_ref_iterator_begin(
*/
snapshot = get_snapshot(refs);
+ if (!snapshot)
+ return empty_ref_iterator_begin();
+
if (prefix && *prefix)
start = find_reference_location_v1(snapshot, prefix, 0);
else
@@ -522,11 +522,12 @@ struct ref_store;
REF_STORE_MAIN)
#define REF_STORE_FORMAT_FILES (1 << 8) /* can use loose ref files */
-#define REF_STORE_FORMAT_PACKED (1 << 9) /* can use packed-refs file */
+#define REF_STORE_FORMAT_PACKED (1 << 9) /* can use v1 packed-refs file */
+#define REF_STORE_FORMAT_PACKED_V2 (1 << 10) /* can use v2 packed-refs file */
static inline int packed_refs_enabled(int flags)
{
- return flags & REF_STORE_FORMAT_PACKED;
+ return flags & (REF_STORE_FORMAT_PACKED | REF_STORE_FORMAT_PACKED_V2);
}
/*
@@ -64,6 +64,7 @@ struct repo_path_cache {
enum ref_format_flags {
REF_FORMAT_FILES = (1 << 0),
REF_FORMAT_PACKED = (1 << 1),
+ REF_FORMAT_PACKED_V2 = (1 << 2),
};
struct repository {
@@ -582,6 +582,8 @@ static enum extension_result handle_extension(const char *var,
data->ref_format |= REF_FORMAT_FILES;
else if (!strcmp(value, "packed"))
data->ref_format |= REF_FORMAT_PACKED;
+ else if (!strcmp(value, "packed-v2"))
+ data->ref_format |= REF_FORMAT_PACKED_V2;
else
return error(_("invalid value for '%s': '%s'"),
"extensions.refFormat", value);
@@ -56,4 +56,23 @@ test_expect_success 'extensions.refFormat=files only' '
)
'
+test_expect_success 'extensions.refFormat=files,packed-v2' '
+ test_commit Q &&
+ git pack-refs --all &&
+ git init no-packed-v1 &&
+ (
+ cd no-packed-v1 &&
+ git config core.repositoryFormatVersion 1 &&
+ git config extensions.refFormat files &&
+ git config --add extensions.refFormat packed-v2 &&
+ test_commit A &&
+ test_commit B &&
+
+ # Refuse to parse a v1 packed-refs file.
+ cp ../.git/packed-refs .git/packed-refs &&
+ test_must_fail git rev-parse refs/tags/Q &&
+ rm -f .git/packed-refs
+ )
+'
+
test_done