зеркало из https://github.com/microsoft/git.git
Merge branch 'ak/corrected-commit-date'
The commit-graph learned to use corrected commit dates instead of the generation number to help topological revision traversal. * ak/corrected-commit-date: doc: add corrected commit date info commit-reach: use corrected commit dates in paint_down_to_common() commit-graph: use generation v2 only if entire chain does commit-graph: implement generation data chunk commit-graph: implement corrected commit date commit-graph: return 64-bit generation number commit-graph: add a slab to store topological levels t6600-test-reach: generalize *_three_modes commit-graph: consolidate fill_commit_graph_info revision: parse parent in indegree_walk_step() commit-graph: fix regression when computing Bloom filters
This commit is contained in:
Коммит
8b4701ae4f
|
@ -4,11 +4,7 @@ Git commit graph format
|
||||||
The Git commit graph stores a list of commit OIDs and some associated
|
The Git commit graph stores a list of commit OIDs and some associated
|
||||||
metadata, including:
|
metadata, including:
|
||||||
|
|
||||||
- The generation number of the commit. Commits with no parents have
|
- The generation number of the commit.
|
||||||
generation number 1; commits with parents have generation number
|
|
||||||
one more than the maximum generation number of its parents. We
|
|
||||||
reserve zero as special, and can be used to mark a generation
|
|
||||||
number invalid or as "not computed".
|
|
||||||
|
|
||||||
- The root tree OID.
|
- The root tree OID.
|
||||||
|
|
||||||
|
@ -86,13 +82,33 @@ CHUNK DATA:
|
||||||
position. If there are more than two parents, the second value
|
position. If there are more than two parents, the second value
|
||||||
has its most-significant bit on and the other bits store an array
|
has its most-significant bit on and the other bits store an array
|
||||||
position into the Extra Edge List chunk.
|
position into the Extra Edge List chunk.
|
||||||
* The next 8 bytes store the generation number of the commit and
|
* The next 8 bytes store the topological level (generation number v1)
|
||||||
|
of the commit and
|
||||||
the commit time in seconds since EPOCH. The generation number
|
the commit time in seconds since EPOCH. The generation number
|
||||||
uses the higher 30 bits of the first 4 bytes, while the commit
|
uses the higher 30 bits of the first 4 bytes, while the commit
|
||||||
time uses the 32 bits of the second 4 bytes, along with the lowest
|
time uses the 32 bits of the second 4 bytes, along with the lowest
|
||||||
2 bits of the lowest byte, storing the 33rd and 34th bit of the
|
2 bits of the lowest byte, storing the 33rd and 34th bit of the
|
||||||
commit time.
|
commit time.
|
||||||
|
|
||||||
|
Generation Data (ID: {'G', 'D', 'A', 'T' }) (N * 4 bytes) [Optional]
|
||||||
|
* This list of 4-byte values store corrected commit date offsets for the
|
||||||
|
commits, arranged in the same order as commit data chunk.
|
||||||
|
* If the corrected commit date offset cannot be stored within 31 bits,
|
||||||
|
the value has its most-significant bit on and the other bits store
|
||||||
|
the position of corrected commit date into the Generation Data Overflow
|
||||||
|
chunk.
|
||||||
|
* Generation Data chunk is present only when commit-graph file is written
|
||||||
|
by compatible versions of Git and in case of split commit-graph chains,
|
||||||
|
the topmost layer also has Generation Data chunk.
|
||||||
|
|
||||||
|
Generation Data Overflow (ID: {'G', 'D', 'O', 'V' }) [Optional]
|
||||||
|
* This list of 8-byte values stores the corrected commit date offsets
|
||||||
|
for commits with corrected commit date offsets that cannot be
|
||||||
|
stored within 31 bits.
|
||||||
|
* Generation Data Overflow chunk is present only when Generation Data
|
||||||
|
chunk is present and atleast one corrected commit date offset cannot
|
||||||
|
be stored within 31 bits.
|
||||||
|
|
||||||
Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
|
Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
|
||||||
This list of 4-byte values store the second through nth parents for
|
This list of 4-byte values store the second through nth parents for
|
||||||
all octopus merges. The second parent value in the commit data stores
|
all octopus merges. The second parent value in the commit data stores
|
||||||
|
|
|
@ -38,14 +38,31 @@ A consumer may load the following info for a commit from the graph:
|
||||||
|
|
||||||
Values 1-4 satisfy the requirements of parse_commit_gently().
|
Values 1-4 satisfy the requirements of parse_commit_gently().
|
||||||
|
|
||||||
Define the "generation number" of a commit recursively as follows:
|
There are two definitions of generation number:
|
||||||
|
1. Corrected committer dates (generation number v2)
|
||||||
|
2. Topological levels (generation nummber v1)
|
||||||
|
|
||||||
* A commit with no parents (a root commit) has generation number one.
|
Define "corrected committer date" of a commit recursively as follows:
|
||||||
|
|
||||||
* A commit with at least one parent has generation number one more than
|
* A commit with no parents (a root commit) has corrected committer date
|
||||||
the largest generation number among its parents.
|
equal to its committer date.
|
||||||
|
|
||||||
Equivalently, the generation number of a commit A is one more than the
|
* A commit with at least one parent has corrected committer date equal to
|
||||||
|
the maximum of its commiter date and one more than the largest corrected
|
||||||
|
committer date among its parents.
|
||||||
|
|
||||||
|
* As a special case, a root commit with timestamp zero has corrected commit
|
||||||
|
date of 1, to be able to distinguish it from GENERATION_NUMBER_ZERO
|
||||||
|
(that is, an uncomputed corrected commit date).
|
||||||
|
|
||||||
|
Define the "topological level" of a commit recursively as follows:
|
||||||
|
|
||||||
|
* A commit with no parents (a root commit) has topological level of one.
|
||||||
|
|
||||||
|
* A commit with at least one parent has topological level one more than
|
||||||
|
the largest topological level among its parents.
|
||||||
|
|
||||||
|
Equivalently, the topological level of a commit A is one more than the
|
||||||
length of a longest path from A to a root commit. The recursive definition
|
length of a longest path from A to a root commit. The recursive definition
|
||||||
is easier to use for computation and observing the following property:
|
is easier to use for computation and observing the following property:
|
||||||
|
|
||||||
|
@ -60,6 +77,9 @@ is easier to use for computation and observing the following property:
|
||||||
generation numbers, then we always expand the boundary commit with highest
|
generation numbers, then we always expand the boundary commit with highest
|
||||||
generation number and can easily detect the stopping condition.
|
generation number and can easily detect the stopping condition.
|
||||||
|
|
||||||
|
The property applies to both versions of generation number, that is both
|
||||||
|
corrected committer dates and topological levels.
|
||||||
|
|
||||||
This property can be used to significantly reduce the time it takes to
|
This property can be used to significantly reduce the time it takes to
|
||||||
walk commits and determine topological relationships. Without generation
|
walk commits and determine topological relationships. Without generation
|
||||||
numbers, the general heuristic is the following:
|
numbers, the general heuristic is the following:
|
||||||
|
@ -67,7 +87,9 @@ numbers, the general heuristic is the following:
|
||||||
If A and B are commits with commit time X and Y, respectively, and
|
If A and B are commits with commit time X and Y, respectively, and
|
||||||
X < Y, then A _probably_ cannot reach B.
|
X < Y, then A _probably_ cannot reach B.
|
||||||
|
|
||||||
This heuristic is currently used whenever the computation is allowed to
|
In absence of corrected commit dates (for example, old versions of Git or
|
||||||
|
mixed generation graph chains),
|
||||||
|
this heuristic is currently used whenever the computation is allowed to
|
||||||
violate topological relationships due to clock skew (such as "git log"
|
violate topological relationships due to clock skew (such as "git log"
|
||||||
with default order), but is not used when the topological order is
|
with default order), but is not used when the topological order is
|
||||||
required (such as merge base calculations, "git log --graph").
|
required (such as merge base calculations, "git log --graph").
|
||||||
|
@ -77,7 +99,7 @@ in the commit graph. We can treat these commits as having "infinite"
|
||||||
generation number and walk until reaching commits with known generation
|
generation number and walk until reaching commits with known generation
|
||||||
number.
|
number.
|
||||||
|
|
||||||
We use the macro GENERATION_NUMBER_INFINITY = 0xFFFFFFFF to mark commits not
|
We use the macro GENERATION_NUMBER_INFINITY to mark commits not
|
||||||
in the commit-graph file. If a commit-graph file was written by a version
|
in the commit-graph file. If a commit-graph file was written by a version
|
||||||
of Git that did not compute generation numbers, then those commits will
|
of Git that did not compute generation numbers, then those commits will
|
||||||
have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
|
have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
|
||||||
|
@ -93,12 +115,12 @@ fully-computed generation numbers. Using strict inequality may result in
|
||||||
walking a few extra commits, but the simplicity in dealing with commits
|
walking a few extra commits, but the simplicity in dealing with commits
|
||||||
with generation number *_INFINITY or *_ZERO is valuable.
|
with generation number *_INFINITY or *_ZERO is valuable.
|
||||||
|
|
||||||
We use the macro GENERATION_NUMBER_MAX = 0x3FFFFFFF to for commits whose
|
We use the macro GENERATION_NUMBER_V1_MAX = 0x3FFFFFFF for commits whose
|
||||||
generation numbers are computed to be at least this value. We limit at
|
topological levels (generation number v1) are computed to be at least
|
||||||
this value since it is the largest value that can be stored in the
|
this value. We limit at this value since it is the largest value that
|
||||||
commit-graph file using the 30 bits available to generation numbers. This
|
can be stored in the commit-graph file using the 30 bits available
|
||||||
presents another case where a commit can have generation number equal to
|
to topological levels. This presents another case where a commit can
|
||||||
that of a parent.
|
have generation number equal to that of a parent.
|
||||||
|
|
||||||
Design Details
|
Design Details
|
||||||
--------------
|
--------------
|
||||||
|
@ -267,6 +289,35 @@ The merge strategy values (2 for the size multiple, 64,000 for the maximum
|
||||||
number of commits) could be extracted into config settings for full
|
number of commits) could be extracted into config settings for full
|
||||||
flexibility.
|
flexibility.
|
||||||
|
|
||||||
|
## Handling Mixed Generation Number Chains
|
||||||
|
|
||||||
|
With the introduction of generation number v2 and generation data chunk, the
|
||||||
|
following scenario is possible:
|
||||||
|
|
||||||
|
1. "New" Git writes a commit-graph with the corrected commit dates.
|
||||||
|
2. "Old" Git writes a split commit-graph on top without corrected commit dates.
|
||||||
|
|
||||||
|
A naive approach of using the newest available generation number from
|
||||||
|
each layer would lead to violated expectations: the lower layer would
|
||||||
|
use corrected commit dates which are much larger than the topological
|
||||||
|
levels of the higher layer. For this reason, Git inspects the topmost
|
||||||
|
layer to see if the layer is missing corrected commit dates. In such a case
|
||||||
|
Git only uses topological level for generation numbers.
|
||||||
|
|
||||||
|
When writing a new layer in split commit-graph, we write corrected commit
|
||||||
|
dates if the topmost layer has corrected commit dates written. This
|
||||||
|
guarantees that if a layer has corrected commit dates, all lower layers
|
||||||
|
must have corrected commit dates as well.
|
||||||
|
|
||||||
|
When merging layers, we do not consider whether the merged layers had corrected
|
||||||
|
commit dates. Instead, the new layer will have corrected commit dates if the
|
||||||
|
layer below the new layer has corrected commit dates.
|
||||||
|
|
||||||
|
While writing or merging layers, if the new layer is the only layer, it will
|
||||||
|
have corrected commit dates when written by compatible versions of Git. Thus,
|
||||||
|
rewriting split commit-graph as a single file (`--split=replace`) creates a
|
||||||
|
single layer with corrected commit dates.
|
||||||
|
|
||||||
## Deleting graph-{hash} files
|
## Deleting graph-{hash} files
|
||||||
|
|
||||||
After a new tip file is written, some `graph-{hash}` files may no longer
|
After a new tip file is written, some `graph-{hash}` files may no longer
|
||||||
|
|
251
commit-graph.c
251
commit-graph.c
|
@ -38,11 +38,13 @@ void git_test_write_commit_graph_or_die(void)
|
||||||
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
|
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
|
||||||
#define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
|
#define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
|
||||||
#define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
|
#define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
|
||||||
|
#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
|
||||||
|
#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f56 /* "GDOV" */
|
||||||
#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
|
#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
|
||||||
#define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
|
#define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
|
||||||
#define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
|
#define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
|
||||||
#define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
|
#define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
|
||||||
#define MAX_NUM_CHUNKS 7
|
#define MAX_NUM_CHUNKS 9
|
||||||
|
|
||||||
#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
|
#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
|
||||||
|
|
||||||
|
@ -61,9 +63,13 @@ void git_test_write_commit_graph_or_die(void)
|
||||||
#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
|
#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
|
||||||
+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
|
+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
|
||||||
|
|
||||||
|
#define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW (1ULL << 31)
|
||||||
|
|
||||||
/* Remember to update object flag allocation in object.h */
|
/* Remember to update object flag allocation in object.h */
|
||||||
#define REACHABLE (1u<<15)
|
#define REACHABLE (1u<<15)
|
||||||
|
|
||||||
|
define_commit_slab(topo_level_slab, uint32_t);
|
||||||
|
|
||||||
/* Keep track of the order in which commits are added to our list. */
|
/* Keep track of the order in which commits are added to our list. */
|
||||||
define_commit_slab(commit_pos, int);
|
define_commit_slab(commit_pos, int);
|
||||||
static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos);
|
static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos);
|
||||||
|
@ -99,7 +105,7 @@ uint32_t commit_graph_position(const struct commit *c)
|
||||||
return data ? data->graph_pos : COMMIT_NOT_FROM_GRAPH;
|
return data ? data->graph_pos : COMMIT_NOT_FROM_GRAPH;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t commit_graph_generation(const struct commit *c)
|
timestamp_t commit_graph_generation(const struct commit *c)
|
||||||
{
|
{
|
||||||
struct commit_graph_data *data =
|
struct commit_graph_data *data =
|
||||||
commit_graph_data_slab_peek(&commit_graph_data_slab, c);
|
commit_graph_data_slab_peek(&commit_graph_data_slab, c);
|
||||||
|
@ -139,13 +145,17 @@ static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Should be used only while writing commit-graph as it compares
|
||||||
|
* generation value of commits by directly accessing commit-slab.
|
||||||
|
*/
|
||||||
static int commit_gen_cmp(const void *va, const void *vb)
|
static int commit_gen_cmp(const void *va, const void *vb)
|
||||||
{
|
{
|
||||||
const struct commit *a = *(const struct commit **)va;
|
const struct commit *a = *(const struct commit **)va;
|
||||||
const struct commit *b = *(const struct commit **)vb;
|
const struct commit *b = *(const struct commit **)vb;
|
||||||
|
|
||||||
uint32_t generation_a = commit_graph_generation(a);
|
const timestamp_t generation_a = commit_graph_data_at(a)->generation;
|
||||||
uint32_t generation_b = commit_graph_generation(b);
|
const timestamp_t generation_b = commit_graph_data_at(b)->generation;
|
||||||
/* lower generation commits first */
|
/* lower generation commits first */
|
||||||
if (generation_a < generation_b)
|
if (generation_a < generation_b)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -388,6 +398,20 @@ struct commit_graph *parse_commit_graph(struct repository *r,
|
||||||
graph->chunk_commit_data = data + chunk_offset;
|
graph->chunk_commit_data = data + chunk_offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GRAPH_CHUNKID_GENERATION_DATA:
|
||||||
|
if (graph->chunk_generation_data)
|
||||||
|
chunk_repeated = 1;
|
||||||
|
else
|
||||||
|
graph->chunk_generation_data = data + chunk_offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW:
|
||||||
|
if (graph->chunk_generation_data_overflow)
|
||||||
|
chunk_repeated = 1;
|
||||||
|
else
|
||||||
|
graph->chunk_generation_data_overflow = data + chunk_offset;
|
||||||
|
break;
|
||||||
|
|
||||||
case GRAPH_CHUNKID_EXTRAEDGES:
|
case GRAPH_CHUNKID_EXTRAEDGES:
|
||||||
if (graph->chunk_extra_edges)
|
if (graph->chunk_extra_edges)
|
||||||
chunk_repeated = 1;
|
chunk_repeated = 1;
|
||||||
|
@ -590,6 +614,21 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
|
||||||
return graph_chain;
|
return graph_chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void validate_mixed_generation_chain(struct commit_graph *g)
|
||||||
|
{
|
||||||
|
int read_generation_data;
|
||||||
|
|
||||||
|
if (!g)
|
||||||
|
return;
|
||||||
|
|
||||||
|
read_generation_data = !!g->chunk_generation_data;
|
||||||
|
|
||||||
|
while (g) {
|
||||||
|
g->read_generation_data = read_generation_data;
|
||||||
|
g = g->base_graph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct commit_graph *read_commit_graph_one(struct repository *r,
|
struct commit_graph *read_commit_graph_one(struct repository *r,
|
||||||
struct object_directory *odb)
|
struct object_directory *odb)
|
||||||
{
|
{
|
||||||
|
@ -598,6 +637,8 @@ struct commit_graph *read_commit_graph_one(struct repository *r,
|
||||||
if (!g)
|
if (!g)
|
||||||
g = load_commit_graph_chain(r, odb);
|
g = load_commit_graph_chain(r, odb);
|
||||||
|
|
||||||
|
validate_mixed_generation_chain(g);
|
||||||
|
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,6 +714,20 @@ int generation_numbers_enabled(struct repository *r)
|
||||||
return !!first_generation;
|
return !!first_generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int corrected_commit_dates_enabled(struct repository *r)
|
||||||
|
{
|
||||||
|
struct commit_graph *g;
|
||||||
|
if (!prepare_commit_graph(r))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
g = r->objects->commit_graph;
|
||||||
|
|
||||||
|
if (!g->num_commits)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return g->read_generation_data;
|
||||||
|
}
|
||||||
|
|
||||||
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
|
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
|
||||||
{
|
{
|
||||||
struct commit_graph *g = r->objects->commit_graph;
|
struct commit_graph *g = r->objects->commit_graph;
|
||||||
|
@ -748,17 +803,41 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
|
||||||
{
|
{
|
||||||
const unsigned char *commit_data;
|
const unsigned char *commit_data;
|
||||||
struct commit_graph_data *graph_data;
|
struct commit_graph_data *graph_data;
|
||||||
uint32_t lex_index;
|
uint32_t lex_index, offset_pos;
|
||||||
|
uint64_t date_high, date_low, offset;
|
||||||
|
|
||||||
while (pos < g->num_commits_in_base)
|
while (pos < g->num_commits_in_base)
|
||||||
g = g->base_graph;
|
g = g->base_graph;
|
||||||
|
|
||||||
|
if (pos >= g->num_commits + g->num_commits_in_base)
|
||||||
|
die(_("invalid commit position. commit-graph is likely corrupt"));
|
||||||
|
|
||||||
lex_index = pos - g->num_commits_in_base;
|
lex_index = pos - g->num_commits_in_base;
|
||||||
commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
|
commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
|
||||||
|
|
||||||
graph_data = commit_graph_data_at(item);
|
graph_data = commit_graph_data_at(item);
|
||||||
graph_data->graph_pos = pos;
|
graph_data->graph_pos = pos;
|
||||||
graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
|
|
||||||
|
date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
|
||||||
|
date_low = get_be32(commit_data + g->hash_len + 12);
|
||||||
|
item->date = (timestamp_t)((date_high << 32) | date_low);
|
||||||
|
|
||||||
|
if (g->read_generation_data) {
|
||||||
|
offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
|
||||||
|
|
||||||
|
if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
|
||||||
|
if (!g->chunk_generation_data_overflow)
|
||||||
|
die(_("commit-graph requires overflow generation data but has none"));
|
||||||
|
|
||||||
|
offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
|
||||||
|
graph_data->generation = get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
|
||||||
|
} else
|
||||||
|
graph_data->generation = item->date + offset;
|
||||||
|
} else
|
||||||
|
graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
|
||||||
|
|
||||||
|
if (g->topo_levels)
|
||||||
|
*topo_level_slab_at(g->topo_levels, item) = get_be32(commit_data + g->hash_len + 8) >> 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void set_commit_tree(struct commit *c, struct tree *t)
|
static inline void set_commit_tree(struct commit *c, struct tree *t)
|
||||||
|
@ -772,38 +851,22 @@ static int fill_commit_in_graph(struct repository *r,
|
||||||
{
|
{
|
||||||
uint32_t edge_value;
|
uint32_t edge_value;
|
||||||
uint32_t *parent_data_ptr;
|
uint32_t *parent_data_ptr;
|
||||||
uint64_t date_low, date_high;
|
|
||||||
struct commit_list **pptr;
|
struct commit_list **pptr;
|
||||||
struct commit_graph_data *graph_data;
|
|
||||||
const unsigned char *commit_data;
|
const unsigned char *commit_data;
|
||||||
uint32_t lex_index;
|
uint32_t lex_index;
|
||||||
|
|
||||||
while (pos < g->num_commits_in_base)
|
while (pos < g->num_commits_in_base)
|
||||||
g = g->base_graph;
|
g = g->base_graph;
|
||||||
|
|
||||||
if (pos >= g->num_commits + g->num_commits_in_base)
|
fill_commit_graph_info(item, g, pos);
|
||||||
die(_("invalid commit position. commit-graph is likely corrupt"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Store the "full" position, but then use the
|
|
||||||
* "local" position for the rest of the calculation.
|
|
||||||
*/
|
|
||||||
graph_data = commit_graph_data_at(item);
|
|
||||||
graph_data->graph_pos = pos;
|
|
||||||
lex_index = pos - g->num_commits_in_base;
|
lex_index = pos - g->num_commits_in_base;
|
||||||
|
|
||||||
commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
|
commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
|
||||||
|
|
||||||
item->object.parsed = 1;
|
item->object.parsed = 1;
|
||||||
|
|
||||||
set_commit_tree(item, NULL);
|
set_commit_tree(item, NULL);
|
||||||
|
|
||||||
date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
|
|
||||||
date_low = get_be32(commit_data + g->hash_len + 12);
|
|
||||||
item->date = (timestamp_t)((date_high << 32) | date_low);
|
|
||||||
|
|
||||||
graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
|
|
||||||
|
|
||||||
pptr = &item->parents;
|
pptr = &item->parents;
|
||||||
|
|
||||||
edge_value = get_be32(commit_data + g->hash_len);
|
edge_value = get_be32(commit_data + g->hash_len);
|
||||||
|
@ -943,6 +1006,7 @@ struct write_commit_graph_context {
|
||||||
struct oid_array oids;
|
struct oid_array oids;
|
||||||
struct packed_commit_list commits;
|
struct packed_commit_list commits;
|
||||||
int num_extra_edges;
|
int num_extra_edges;
|
||||||
|
int num_generation_data_overflows;
|
||||||
unsigned long approx_nr_objects;
|
unsigned long approx_nr_objects;
|
||||||
struct progress *progress;
|
struct progress *progress;
|
||||||
int progress_done;
|
int progress_done;
|
||||||
|
@ -961,8 +1025,10 @@ struct write_commit_graph_context {
|
||||||
report_progress:1,
|
report_progress:1,
|
||||||
split:1,
|
split:1,
|
||||||
changed_paths:1,
|
changed_paths:1,
|
||||||
order_by_pack:1;
|
order_by_pack:1,
|
||||||
|
write_generation_data:1;
|
||||||
|
|
||||||
|
struct topo_level_slab *topo_levels;
|
||||||
const struct commit_graph_opts *opts;
|
const struct commit_graph_opts *opts;
|
||||||
size_t total_bloom_filter_data_size;
|
size_t total_bloom_filter_data_size;
|
||||||
const struct bloom_filter_settings *bloom_settings;
|
const struct bloom_filter_settings *bloom_settings;
|
||||||
|
@ -1109,7 +1175,7 @@ static int write_graph_chunk_data(struct hashfile *f,
|
||||||
else
|
else
|
||||||
packedDate[0] = 0;
|
packedDate[0] = 0;
|
||||||
|
|
||||||
packedDate[0] |= htonl(commit_graph_data_at(*list)->generation << 2);
|
packedDate[0] |= htonl(*topo_level_slab_at(ctx->topo_levels, *list) << 2);
|
||||||
|
|
||||||
packedDate[1] = htonl((*list)->date);
|
packedDate[1] = htonl((*list)->date);
|
||||||
hashwrite(f, packedDate, 8);
|
hashwrite(f, packedDate, 8);
|
||||||
|
@ -1120,6 +1186,45 @@ static int write_graph_chunk_data(struct hashfile *f,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_graph_chunk_generation_data(struct hashfile *f,
|
||||||
|
struct write_commit_graph_context *ctx)
|
||||||
|
{
|
||||||
|
int i, num_generation_data_overflows = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->commits.nr; i++) {
|
||||||
|
struct commit *c = ctx->commits.list[i];
|
||||||
|
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
|
||||||
|
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||||
|
|
||||||
|
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
||||||
|
offset = CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW | num_generation_data_overflows;
|
||||||
|
num_generation_data_overflows++;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashwrite_be32(f, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
|
||||||
|
struct write_commit_graph_context *ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ctx->commits.nr; i++) {
|
||||||
|
struct commit *c = ctx->commits.list[i];
|
||||||
|
timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
|
||||||
|
display_progress(ctx->progress, ++ctx->progress_cnt);
|
||||||
|
|
||||||
|
if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
|
||||||
|
hashwrite_be32(f, offset >> 32);
|
||||||
|
hashwrite_be32(f, (uint32_t) offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int write_graph_chunk_extra_edges(struct hashfile *f,
|
static int write_graph_chunk_extra_edges(struct hashfile *f,
|
||||||
struct write_commit_graph_context *ctx)
|
struct write_commit_graph_context *ctx)
|
||||||
{
|
{
|
||||||
|
@ -1339,11 +1444,12 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
|
||||||
_("Computing commit graph generation numbers"),
|
_("Computing commit graph generation numbers"),
|
||||||
ctx->commits.nr);
|
ctx->commits.nr);
|
||||||
for (i = 0; i < ctx->commits.nr; i++) {
|
for (i = 0; i < ctx->commits.nr; i++) {
|
||||||
uint32_t generation = commit_graph_data_at(ctx->commits.list[i])->generation;
|
uint32_t level = *topo_level_slab_at(ctx->topo_levels, ctx->commits.list[i]);
|
||||||
|
timestamp_t corrected_commit_date = commit_graph_data_at(ctx->commits.list[i])->generation;
|
||||||
|
|
||||||
display_progress(ctx->progress, i + 1);
|
display_progress(ctx->progress, i + 1);
|
||||||
if (generation != GENERATION_NUMBER_INFINITY &&
|
if (level != GENERATION_NUMBER_ZERO &&
|
||||||
generation != GENERATION_NUMBER_ZERO)
|
corrected_commit_date != GENERATION_NUMBER_ZERO)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
commit_list_insert(ctx->commits.list[i], &list);
|
commit_list_insert(ctx->commits.list[i], &list);
|
||||||
|
@ -1351,29 +1457,40 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
|
||||||
struct commit *current = list->item;
|
struct commit *current = list->item;
|
||||||
struct commit_list *parent;
|
struct commit_list *parent;
|
||||||
int all_parents_computed = 1;
|
int all_parents_computed = 1;
|
||||||
uint32_t max_generation = 0;
|
uint32_t max_level = 0;
|
||||||
|
timestamp_t max_corrected_commit_date = 0;
|
||||||
|
|
||||||
for (parent = current->parents; parent; parent = parent->next) {
|
for (parent = current->parents; parent; parent = parent->next) {
|
||||||
generation = commit_graph_data_at(parent->item)->generation;
|
level = *topo_level_slab_at(ctx->topo_levels, parent->item);
|
||||||
|
corrected_commit_date = commit_graph_data_at(parent->item)->generation;
|
||||||
|
|
||||||
if (generation == GENERATION_NUMBER_INFINITY ||
|
if (level == GENERATION_NUMBER_ZERO ||
|
||||||
generation == GENERATION_NUMBER_ZERO) {
|
corrected_commit_date == GENERATION_NUMBER_ZERO) {
|
||||||
all_parents_computed = 0;
|
all_parents_computed = 0;
|
||||||
commit_list_insert(parent->item, &list);
|
commit_list_insert(parent->item, &list);
|
||||||
break;
|
break;
|
||||||
} else if (generation > max_generation) {
|
|
||||||
max_generation = generation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (level > max_level)
|
||||||
|
max_level = level;
|
||||||
|
|
||||||
|
if (corrected_commit_date > max_corrected_commit_date)
|
||||||
|
max_corrected_commit_date = corrected_commit_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_parents_computed) {
|
if (all_parents_computed) {
|
||||||
struct commit_graph_data *data = commit_graph_data_at(current);
|
|
||||||
|
|
||||||
data->generation = max_generation + 1;
|
|
||||||
pop_commit(&list);
|
pop_commit(&list);
|
||||||
|
|
||||||
if (data->generation > GENERATION_NUMBER_MAX)
|
if (max_level > GENERATION_NUMBER_V1_MAX - 1)
|
||||||
data->generation = GENERATION_NUMBER_MAX;
|
max_level = GENERATION_NUMBER_V1_MAX - 1;
|
||||||
|
*topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
|
||||||
|
|
||||||
|
if (current->date && current->date > max_corrected_commit_date)
|
||||||
|
max_corrected_commit_date = current->date - 1;
|
||||||
|
commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
|
||||||
|
|
||||||
|
if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
|
||||||
|
ctx->num_generation_data_overflows++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1707,6 +1824,21 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
|
||||||
chunks[2].id = GRAPH_CHUNKID_DATA;
|
chunks[2].id = GRAPH_CHUNKID_DATA;
|
||||||
chunks[2].size = (hashsz + 16) * ctx->commits.nr;
|
chunks[2].size = (hashsz + 16) * ctx->commits.nr;
|
||||||
chunks[2].write_fn = write_graph_chunk_data;
|
chunks[2].write_fn = write_graph_chunk_data;
|
||||||
|
|
||||||
|
if (git_env_bool(GIT_TEST_COMMIT_GRAPH_NO_GDAT, 0))
|
||||||
|
ctx->write_generation_data = 0;
|
||||||
|
if (ctx->write_generation_data) {
|
||||||
|
chunks[num_chunks].id = GRAPH_CHUNKID_GENERATION_DATA;
|
||||||
|
chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
|
||||||
|
chunks[num_chunks].write_fn = write_graph_chunk_generation_data;
|
||||||
|
num_chunks++;
|
||||||
|
}
|
||||||
|
if (ctx->num_generation_data_overflows) {
|
||||||
|
chunks[num_chunks].id = GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW;
|
||||||
|
chunks[num_chunks].size = sizeof(timestamp_t) * ctx->num_generation_data_overflows;
|
||||||
|
chunks[num_chunks].write_fn = write_graph_chunk_generation_data_overflow;
|
||||||
|
num_chunks++;
|
||||||
|
}
|
||||||
if (ctx->num_extra_edges) {
|
if (ctx->num_extra_edges) {
|
||||||
chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
|
chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
|
||||||
chunks[num_chunks].size = 4 * ctx->num_extra_edges;
|
chunks[num_chunks].size = 4 * ctx->num_extra_edges;
|
||||||
|
@ -1918,6 +2050,13 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
|
||||||
if (i < ctx->num_commit_graphs_after)
|
if (i < ctx->num_commit_graphs_after)
|
||||||
ctx->commit_graph_hash_after[i] = xstrdup(oid_to_hex(&g->oid));
|
ctx->commit_graph_hash_after[i] = xstrdup(oid_to_hex(&g->oid));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the topmost remaining layer has generation data chunk, the
|
||||||
|
* resultant layer also has generation data chunk.
|
||||||
|
*/
|
||||||
|
if (i == ctx->num_commit_graphs_after - 2)
|
||||||
|
ctx->write_generation_data = !!g->chunk_generation_data;
|
||||||
|
|
||||||
i--;
|
i--;
|
||||||
g = g->base_graph;
|
g = g->base_graph;
|
||||||
}
|
}
|
||||||
|
@ -2109,6 +2248,7 @@ int write_commit_graph(struct object_directory *odb,
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int replace = 0;
|
int replace = 0;
|
||||||
struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
|
struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
|
||||||
|
struct topo_level_slab topo_levels;
|
||||||
|
|
||||||
prepare_repo_settings(the_repository);
|
prepare_repo_settings(the_repository);
|
||||||
if (!the_repository->settings.core_commit_graph) {
|
if (!the_repository->settings.core_commit_graph) {
|
||||||
|
@ -2126,6 +2266,8 @@ int write_commit_graph(struct object_directory *odb,
|
||||||
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
|
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
|
||||||
ctx->opts = opts;
|
ctx->opts = opts;
|
||||||
ctx->total_bloom_filter_data_size = 0;
|
ctx->total_bloom_filter_data_size = 0;
|
||||||
|
ctx->write_generation_data = 1;
|
||||||
|
ctx->num_generation_data_overflows = 0;
|
||||||
|
|
||||||
bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
|
bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
|
||||||
bloom_settings.bits_per_entry);
|
bloom_settings.bits_per_entry);
|
||||||
|
@ -2135,6 +2277,18 @@ int write_commit_graph(struct object_directory *odb,
|
||||||
bloom_settings.max_changed_paths);
|
bloom_settings.max_changed_paths);
|
||||||
ctx->bloom_settings = &bloom_settings;
|
ctx->bloom_settings = &bloom_settings;
|
||||||
|
|
||||||
|
init_topo_level_slab(&topo_levels);
|
||||||
|
ctx->topo_levels = &topo_levels;
|
||||||
|
|
||||||
|
if (ctx->r->objects->commit_graph) {
|
||||||
|
struct commit_graph *g = ctx->r->objects->commit_graph;
|
||||||
|
|
||||||
|
while (g) {
|
||||||
|
g->topo_levels = &topo_levels;
|
||||||
|
g = g->base_graph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
|
if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
|
||||||
ctx->changed_paths = 1;
|
ctx->changed_paths = 1;
|
||||||
if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
|
if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
|
||||||
|
@ -2227,6 +2381,8 @@ int write_commit_graph(struct object_directory *odb,
|
||||||
} else
|
} else
|
||||||
ctx->num_commit_graphs_after = 1;
|
ctx->num_commit_graphs_after = 1;
|
||||||
|
|
||||||
|
validate_mixed_generation_chain(ctx->r->objects->commit_graph);
|
||||||
|
|
||||||
compute_generation_numbers(ctx);
|
compute_generation_numbers(ctx);
|
||||||
|
|
||||||
if (ctx->changed_paths)
|
if (ctx->changed_paths)
|
||||||
|
@ -2355,8 +2511,8 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
|
||||||
for (i = 0; i < g->num_commits; i++) {
|
for (i = 0; i < g->num_commits; i++) {
|
||||||
struct commit *graph_commit, *odb_commit;
|
struct commit *graph_commit, *odb_commit;
|
||||||
struct commit_list *graph_parents, *odb_parents;
|
struct commit_list *graph_parents, *odb_parents;
|
||||||
uint32_t max_generation = 0;
|
timestamp_t max_generation = 0;
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
|
|
||||||
display_progress(progress, i + 1);
|
display_progress(progress, i + 1);
|
||||||
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
|
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
|
||||||
|
@ -2420,16 +2576,17 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If one of our parents has generation GENERATION_NUMBER_MAX, then
|
* If we are using topological level and one of our parents has
|
||||||
* our generation is also GENERATION_NUMBER_MAX. Decrement to avoid
|
* generation GENERATION_NUMBER_V1_MAX, then our generation is
|
||||||
* extra logic in the following condition.
|
* also GENERATION_NUMBER_V1_MAX. Decrement to avoid extra logic
|
||||||
|
* in the following condition.
|
||||||
*/
|
*/
|
||||||
if (max_generation == GENERATION_NUMBER_MAX)
|
if (!g->read_generation_data && max_generation == GENERATION_NUMBER_V1_MAX)
|
||||||
max_generation--;
|
max_generation--;
|
||||||
|
|
||||||
generation = commit_graph_generation(graph_commit);
|
generation = commit_graph_generation(graph_commit);
|
||||||
if (generation != max_generation + 1)
|
if (generation < max_generation + 1)
|
||||||
graph_report(_("commit-graph generation for commit %s is %u != %u"),
|
graph_report(_("commit-graph generation for commit %s is %"PRItime" < %"PRItime),
|
||||||
oid_to_hex(&cur_oid),
|
oid_to_hex(&cur_oid),
|
||||||
generation,
|
generation,
|
||||||
max_generation + 1);
|
max_generation + 1);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "oidset.h"
|
#include "oidset.h"
|
||||||
|
|
||||||
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
|
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
|
||||||
|
#define GIT_TEST_COMMIT_GRAPH_NO_GDAT "GIT_TEST_COMMIT_GRAPH_NO_GDAT"
|
||||||
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
|
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
|
||||||
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
|
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
|
||||||
|
|
||||||
|
@ -63,16 +64,20 @@ struct commit_graph {
|
||||||
struct object_directory *odb;
|
struct object_directory *odb;
|
||||||
|
|
||||||
uint32_t num_commits_in_base;
|
uint32_t num_commits_in_base;
|
||||||
|
unsigned int read_generation_data;
|
||||||
struct commit_graph *base_graph;
|
struct commit_graph *base_graph;
|
||||||
|
|
||||||
const uint32_t *chunk_oid_fanout;
|
const uint32_t *chunk_oid_fanout;
|
||||||
const unsigned char *chunk_oid_lookup;
|
const unsigned char *chunk_oid_lookup;
|
||||||
const unsigned char *chunk_commit_data;
|
const unsigned char *chunk_commit_data;
|
||||||
|
const unsigned char *chunk_generation_data;
|
||||||
|
const unsigned char *chunk_generation_data_overflow;
|
||||||
const unsigned char *chunk_extra_edges;
|
const unsigned char *chunk_extra_edges;
|
||||||
const unsigned char *chunk_base_graphs;
|
const unsigned char *chunk_base_graphs;
|
||||||
const unsigned char *chunk_bloom_indexes;
|
const unsigned char *chunk_bloom_indexes;
|
||||||
const unsigned char *chunk_bloom_data;
|
const unsigned char *chunk_bloom_data;
|
||||||
|
|
||||||
|
struct topo_level_slab *topo_levels;
|
||||||
struct bloom_filter_settings *bloom_filter_settings;
|
struct bloom_filter_settings *bloom_filter_settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,6 +95,12 @@ struct commit_graph *parse_commit_graph(struct repository *r,
|
||||||
*/
|
*/
|
||||||
int generation_numbers_enabled(struct repository *r);
|
int generation_numbers_enabled(struct repository *r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return 1 if and only if the repository has a commit-graph
|
||||||
|
* file and generation data chunk has been written for the file.
|
||||||
|
*/
|
||||||
|
int corrected_commit_dates_enabled(struct repository *r);
|
||||||
|
|
||||||
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
|
struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
|
||||||
|
|
||||||
enum commit_graph_write_flags {
|
enum commit_graph_write_flags {
|
||||||
|
@ -144,12 +155,12 @@ void disable_commit_graph(struct repository *r);
|
||||||
|
|
||||||
struct commit_graph_data {
|
struct commit_graph_data {
|
||||||
uint32_t graph_pos;
|
uint32_t graph_pos;
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commits should be parsed before accessing generation, graph positions.
|
* Commits should be parsed before accessing generation, graph positions.
|
||||||
*/
|
*/
|
||||||
uint32_t commit_graph_generation(const struct commit *);
|
timestamp_t commit_graph_generation(const struct commit *);
|
||||||
uint32_t commit_graph_position(const struct commit *);
|
uint32_t commit_graph_position(const struct commit *);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,14 +32,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
|
||||||
static struct commit_list *paint_down_to_common(struct repository *r,
|
static struct commit_list *paint_down_to_common(struct repository *r,
|
||||||
struct commit *one, int n,
|
struct commit *one, int n,
|
||||||
struct commit **twos,
|
struct commit **twos,
|
||||||
int min_generation)
|
timestamp_t min_generation)
|
||||||
{
|
{
|
||||||
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
|
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
|
||||||
struct commit_list *result = NULL;
|
struct commit_list *result = NULL;
|
||||||
int i;
|
int i;
|
||||||
uint32_t last_gen = GENERATION_NUMBER_INFINITY;
|
timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
|
||||||
|
|
||||||
if (!min_generation)
|
if (!min_generation && !corrected_commit_dates_enabled(r))
|
||||||
queue.compare = compare_commits_by_commit_date;
|
queue.compare = compare_commits_by_commit_date;
|
||||||
|
|
||||||
one->object.flags |= PARENT1;
|
one->object.flags |= PARENT1;
|
||||||
|
@ -58,10 +58,10 @@ static struct commit_list *paint_down_to_common(struct repository *r,
|
||||||
struct commit *commit = prio_queue_get(&queue);
|
struct commit *commit = prio_queue_get(&queue);
|
||||||
struct commit_list *parents;
|
struct commit_list *parents;
|
||||||
int flags;
|
int flags;
|
||||||
uint32_t generation = commit_graph_generation(commit);
|
timestamp_t generation = commit_graph_generation(commit);
|
||||||
|
|
||||||
if (min_generation && generation > last_gen)
|
if (min_generation && generation > last_gen)
|
||||||
BUG("bad generation skip %8x > %8x at %s",
|
BUG("bad generation skip %"PRItime" > %"PRItime" at %s",
|
||||||
generation, last_gen,
|
generation, last_gen,
|
||||||
oid_to_hex(&commit->object.oid));
|
oid_to_hex(&commit->object.oid));
|
||||||
last_gen = generation;
|
last_gen = generation;
|
||||||
|
@ -177,12 +177,12 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
|
||||||
repo_parse_commit(r, array[i]);
|
repo_parse_commit(r, array[i]);
|
||||||
for (i = 0; i < cnt; i++) {
|
for (i = 0; i < cnt; i++) {
|
||||||
struct commit_list *common;
|
struct commit_list *common;
|
||||||
uint32_t min_generation = commit_graph_generation(array[i]);
|
timestamp_t min_generation = commit_graph_generation(array[i]);
|
||||||
|
|
||||||
if (redundant[i])
|
if (redundant[i])
|
||||||
continue;
|
continue;
|
||||||
for (j = filled = 0; j < cnt; j++) {
|
for (j = filled = 0; j < cnt; j++) {
|
||||||
uint32_t curr_generation;
|
timestamp_t curr_generation;
|
||||||
if (i == j || redundant[j])
|
if (i == j || redundant[j])
|
||||||
continue;
|
continue;
|
||||||
filled_index[filled] = j;
|
filled_index[filled] = j;
|
||||||
|
@ -321,7 +321,7 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
|
||||||
{
|
{
|
||||||
struct commit_list *bases;
|
struct commit_list *bases;
|
||||||
int ret = 0, i;
|
int ret = 0, i;
|
||||||
uint32_t generation, max_generation = GENERATION_NUMBER_ZERO;
|
timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
|
||||||
|
|
||||||
if (repo_parse_commit(r, commit))
|
if (repo_parse_commit(r, commit))
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -470,7 +470,7 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
|
||||||
static enum contains_result contains_test(struct commit *candidate,
|
static enum contains_result contains_test(struct commit *candidate,
|
||||||
const struct commit_list *want,
|
const struct commit_list *want,
|
||||||
struct contains_cache *cache,
|
struct contains_cache *cache,
|
||||||
uint32_t cutoff)
|
timestamp_t cutoff)
|
||||||
{
|
{
|
||||||
enum contains_result *cached = contains_cache_at(cache, candidate);
|
enum contains_result *cached = contains_cache_at(cache, candidate);
|
||||||
|
|
||||||
|
@ -506,11 +506,11 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
|
||||||
{
|
{
|
||||||
struct contains_stack contains_stack = { 0, 0, NULL };
|
struct contains_stack contains_stack = { 0, 0, NULL };
|
||||||
enum contains_result result;
|
enum contains_result result;
|
||||||
uint32_t cutoff = GENERATION_NUMBER_INFINITY;
|
timestamp_t cutoff = GENERATION_NUMBER_INFINITY;
|
||||||
const struct commit_list *p;
|
const struct commit_list *p;
|
||||||
|
|
||||||
for (p = want; p; p = p->next) {
|
for (p = want; p; p = p->next) {
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
struct commit *c = p->item;
|
struct commit *c = p->item;
|
||||||
load_commit_graph_info(the_repository, c);
|
load_commit_graph_info(the_repository, c);
|
||||||
generation = commit_graph_generation(c);
|
generation = commit_graph_generation(c);
|
||||||
|
@ -566,8 +566,8 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
|
||||||
const struct commit *a = *(const struct commit * const *)_a;
|
const struct commit *a = *(const struct commit * const *)_a;
|
||||||
const struct commit *b = *(const struct commit * const *)_b;
|
const struct commit *b = *(const struct commit * const *)_b;
|
||||||
|
|
||||||
uint32_t generation_a = commit_graph_generation(a);
|
timestamp_t generation_a = commit_graph_generation(a);
|
||||||
uint32_t generation_b = commit_graph_generation(b);
|
timestamp_t generation_b = commit_graph_generation(b);
|
||||||
|
|
||||||
if (generation_a < generation_b)
|
if (generation_a < generation_b)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -580,7 +580,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
|
||||||
unsigned int with_flag,
|
unsigned int with_flag,
|
||||||
unsigned int assign_flag,
|
unsigned int assign_flag,
|
||||||
time_t min_commit_date,
|
time_t min_commit_date,
|
||||||
uint32_t min_generation)
|
timestamp_t min_generation)
|
||||||
{
|
{
|
||||||
struct commit **list = NULL;
|
struct commit **list = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
@ -681,13 +681,13 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
|
||||||
time_t min_commit_date = cutoff_by_min_date ? from->item->date : 0;
|
time_t min_commit_date = cutoff_by_min_date ? from->item->date : 0;
|
||||||
struct commit_list *from_iter = from, *to_iter = to;
|
struct commit_list *from_iter = from, *to_iter = to;
|
||||||
int result;
|
int result;
|
||||||
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
|
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
|
||||||
|
|
||||||
while (from_iter) {
|
while (from_iter) {
|
||||||
add_object_array(&from_iter->item->object, NULL, &from_objs);
|
add_object_array(&from_iter->item->object, NULL, &from_objs);
|
||||||
|
|
||||||
if (!parse_commit(from_iter->item)) {
|
if (!parse_commit(from_iter->item)) {
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
if (from_iter->item->date < min_commit_date)
|
if (from_iter->item->date < min_commit_date)
|
||||||
min_commit_date = from_iter->item->date;
|
min_commit_date = from_iter->item->date;
|
||||||
|
|
||||||
|
@ -701,7 +701,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
|
||||||
|
|
||||||
while (to_iter) {
|
while (to_iter) {
|
||||||
if (!parse_commit(to_iter->item)) {
|
if (!parse_commit(to_iter->item)) {
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
if (to_iter->item->date < min_commit_date)
|
if (to_iter->item->date < min_commit_date)
|
||||||
min_commit_date = to_iter->item->date;
|
min_commit_date = to_iter->item->date;
|
||||||
|
|
||||||
|
@ -741,13 +741,13 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
|
||||||
struct commit_list *found_commits = NULL;
|
struct commit_list *found_commits = NULL;
|
||||||
struct commit **to_last = to + nr_to;
|
struct commit **to_last = to + nr_to;
|
||||||
struct commit **from_last = from + nr_from;
|
struct commit **from_last = from + nr_from;
|
||||||
uint32_t min_generation = GENERATION_NUMBER_INFINITY;
|
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
|
||||||
int num_to_find = 0;
|
int num_to_find = 0;
|
||||||
|
|
||||||
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
|
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
|
||||||
|
|
||||||
for (item = to; item < to_last; item++) {
|
for (item = to; item < to_last; item++) {
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
struct commit *c = *item;
|
struct commit *c = *item;
|
||||||
|
|
||||||
parse_commit(c);
|
parse_commit(c);
|
||||||
|
|
|
@ -87,7 +87,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
|
||||||
unsigned int with_flag,
|
unsigned int with_flag,
|
||||||
unsigned int assign_flag,
|
unsigned int assign_flag,
|
||||||
time_t min_commit_date,
|
time_t min_commit_date,
|
||||||
uint32_t min_generation);
|
timestamp_t min_generation);
|
||||||
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
|
int can_all_from_reach(struct commit_list *from, struct commit_list *to,
|
||||||
int commit_date_cutoff);
|
int commit_date_cutoff);
|
||||||
|
|
||||||
|
|
4
commit.c
4
commit.c
|
@ -753,8 +753,8 @@ int compare_commits_by_author_date(const void *a_, const void *b_,
|
||||||
int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
|
int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
|
||||||
{
|
{
|
||||||
const struct commit *a = a_, *b = b_;
|
const struct commit *a = a_, *b = b_;
|
||||||
const uint32_t generation_a = commit_graph_generation(a),
|
const timestamp_t generation_a = commit_graph_generation(a),
|
||||||
generation_b = commit_graph_generation(b);
|
generation_b = commit_graph_generation(b);
|
||||||
|
|
||||||
/* newer commits first */
|
/* newer commits first */
|
||||||
if (generation_a < generation_b)
|
if (generation_a < generation_b)
|
||||||
|
|
5
commit.h
5
commit.h
|
@ -11,9 +11,10 @@
|
||||||
#include "commit-slab.h"
|
#include "commit-slab.h"
|
||||||
|
|
||||||
#define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
|
#define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
|
||||||
#define GENERATION_NUMBER_INFINITY 0xFFFFFFFF
|
#define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
|
||||||
#define GENERATION_NUMBER_MAX 0x3FFFFFFF
|
#define GENERATION_NUMBER_V1_MAX 0x3FFFFFFF
|
||||||
#define GENERATION_NUMBER_ZERO 0
|
#define GENERATION_NUMBER_ZERO 0
|
||||||
|
#define GENERATION_NUMBER_V2_OFFSET_MAX ((1ULL << 31) - 1)
|
||||||
|
|
||||||
struct commit_list {
|
struct commit_list {
|
||||||
struct commit *item;
|
struct commit *item;
|
||||||
|
|
13
revision.c
13
revision.c
|
@ -3270,7 +3270,7 @@ define_commit_slab(indegree_slab, int);
|
||||||
define_commit_slab(author_date_slab, timestamp_t);
|
define_commit_slab(author_date_slab, timestamp_t);
|
||||||
|
|
||||||
struct topo_walk_info {
|
struct topo_walk_info {
|
||||||
uint32_t min_generation;
|
timestamp_t min_generation;
|
||||||
struct prio_queue explore_queue;
|
struct prio_queue explore_queue;
|
||||||
struct prio_queue indegree_queue;
|
struct prio_queue indegree_queue;
|
||||||
struct prio_queue topo_queue;
|
struct prio_queue topo_queue;
|
||||||
|
@ -3338,7 +3338,7 @@ static void explore_walk_step(struct rev_info *revs)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void explore_to_depth(struct rev_info *revs,
|
static void explore_to_depth(struct rev_info *revs,
|
||||||
uint32_t gen_cutoff)
|
timestamp_t gen_cutoff)
|
||||||
{
|
{
|
||||||
struct topo_walk_info *info = revs->topo_walk_info;
|
struct topo_walk_info *info = revs->topo_walk_info;
|
||||||
struct commit *c;
|
struct commit *c;
|
||||||
|
@ -3367,6 +3367,9 @@ static void indegree_walk_step(struct rev_info *revs)
|
||||||
struct commit *parent = p->item;
|
struct commit *parent = p->item;
|
||||||
int *pi = indegree_slab_at(&info->indegree, parent);
|
int *pi = indegree_slab_at(&info->indegree, parent);
|
||||||
|
|
||||||
|
if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (*pi)
|
if (*pi)
|
||||||
(*pi)++;
|
(*pi)++;
|
||||||
else
|
else
|
||||||
|
@ -3380,7 +3383,7 @@ static void indegree_walk_step(struct rev_info *revs)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compute_indegrees_to_depth(struct rev_info *revs,
|
static void compute_indegrees_to_depth(struct rev_info *revs,
|
||||||
uint32_t gen_cutoff)
|
timestamp_t gen_cutoff)
|
||||||
{
|
{
|
||||||
struct topo_walk_info *info = revs->topo_walk_info;
|
struct topo_walk_info *info = revs->topo_walk_info;
|
||||||
struct commit *c;
|
struct commit *c;
|
||||||
|
@ -3438,7 +3441,7 @@ static void init_topo_walk(struct rev_info *revs)
|
||||||
info->min_generation = GENERATION_NUMBER_INFINITY;
|
info->min_generation = GENERATION_NUMBER_INFINITY;
|
||||||
for (list = revs->commits; list; list = list->next) {
|
for (list = revs->commits; list; list = list->next) {
|
||||||
struct commit *c = list->item;
|
struct commit *c = list->item;
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
|
|
||||||
if (repo_parse_commit_gently(revs->repo, c, 1))
|
if (repo_parse_commit_gently(revs->repo, c, 1))
|
||||||
continue;
|
continue;
|
||||||
|
@ -3506,7 +3509,7 @@ static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
|
||||||
for (p = commit->parents; p; p = p->next) {
|
for (p = commit->parents; p; p = p->next) {
|
||||||
struct commit *parent = p->item;
|
struct commit *parent = p->item;
|
||||||
int *pi;
|
int *pi;
|
||||||
uint32_t generation;
|
timestamp_t generation;
|
||||||
|
|
||||||
if (parent->object.flags & UNINTERESTING)
|
if (parent->object.flags & UNINTERESTING)
|
||||||
continue;
|
continue;
|
||||||
|
|
3
t/README
3
t/README
|
@ -387,6 +387,9 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
|
||||||
be written after every 'git commit' command, and overrides the
|
be written after every 'git commit' command, and overrides the
|
||||||
'core.commitGraph' setting to true.
|
'core.commitGraph' setting to true.
|
||||||
|
|
||||||
|
GIT_TEST_COMMIT_GRAPH_NO_GDAT=<boolean>, when true, forces the
|
||||||
|
commit-graph to be written without generation data chunk.
|
||||||
|
|
||||||
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
|
GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
|
||||||
commit-graph write to compute and write changed path Bloom filters for
|
commit-graph write to compute and write changed path Bloom filters for
|
||||||
every 'git commit-graph write', as if the `--changed-paths` option was
|
every 'git commit-graph write', as if the `--changed-paths` option was
|
||||||
|
|
|
@ -33,6 +33,10 @@ int cmd__read_graph(int argc, const char **argv)
|
||||||
printf(" oid_lookup");
|
printf(" oid_lookup");
|
||||||
if (graph->chunk_commit_data)
|
if (graph->chunk_commit_data)
|
||||||
printf(" commit_metadata");
|
printf(" commit_metadata");
|
||||||
|
if (graph->chunk_generation_data)
|
||||||
|
printf(" generation_data");
|
||||||
|
if (graph->chunk_generation_data_overflow)
|
||||||
|
printf(" generation_data_overflow");
|
||||||
if (graph->chunk_extra_edges)
|
if (graph->chunk_extra_edges)
|
||||||
printf(" extra_edges");
|
printf(" extra_edges");
|
||||||
if (graph->chunk_bloom_indexes)
|
if (graph->chunk_bloom_indexes)
|
||||||
|
|
|
@ -43,11 +43,11 @@ test_expect_success 'setup test - repo, commits, commit graph, log outputs' '
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_read_expect () {
|
graph_read_expect () {
|
||||||
NUM_CHUNKS=5
|
NUM_CHUNKS=6
|
||||||
cat >expect <<- EOF
|
cat >expect <<- EOF
|
||||||
header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
|
header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
|
||||||
num_commits: $1
|
num_commits: $1
|
||||||
chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data
|
chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
|
||||||
EOF
|
EOF
|
||||||
test-tool read-graph >actual &&
|
test-tool read-graph >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
|
|
|
@ -431,15 +431,33 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
|
test_expect_success TIME_IS_64BIT 'set up repository with far-future (2^34 - 1) commit' '
|
||||||
rm -f .git/index &&
|
rm -f .git/index &&
|
||||||
echo content >file &&
|
echo foo >file &&
|
||||||
git add file &&
|
git add file &&
|
||||||
GIT_COMMITTER_DATE="@68719476737 +0000" \
|
GIT_COMMITTER_DATE="@17179869183 +0000" \
|
||||||
git commit -m "tempori parendum"
|
git commit -m "tempori parendum"
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
|
test_expect_success TIME_IS_64BIT 'generate tar with far-future mtime' '
|
||||||
|
git archive HEAD >future.tar
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
|
||||||
|
echo 2514 >expect &&
|
||||||
|
tar_info future.tar | cut -d" " -f2 >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success TIME_IS_64BIT 'set up repository with far-far-future (2^36 + 1) commit' '
|
||||||
|
rm -f .git/index &&
|
||||||
|
echo content >file &&
|
||||||
|
git add file &&
|
||||||
|
GIT_TEST_COMMIT_GRAPH=0 GIT_COMMITTER_DATE="@68719476737 +0000" \
|
||||||
|
git commit -m "tempori parendum"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success TIME_IS_64BIT 'generate tar with far-far-future mtime' '
|
||||||
git archive HEAD >future.tar
|
git archive HEAD >future.tar
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ graph_git_behavior 'no graph' full commits/3 commits/1
|
||||||
graph_read_expect() {
|
graph_read_expect() {
|
||||||
OPTIONAL=""
|
OPTIONAL=""
|
||||||
NUM_CHUNKS=3
|
NUM_CHUNKS=3
|
||||||
if test ! -z $2
|
if test ! -z "$2"
|
||||||
then
|
then
|
||||||
OPTIONAL=" $2"
|
OPTIONAL=" $2"
|
||||||
NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
|
NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
|
||||||
|
@ -103,14 +103,14 @@ test_expect_success 'exit with correct error on bad input to --stdin-commits' '
|
||||||
# valid commit and tree OID
|
# valid commit and tree OID
|
||||||
git rev-parse HEAD HEAD^{tree} >in &&
|
git rev-parse HEAD HEAD^{tree} >in &&
|
||||||
git commit-graph write --stdin-commits <in &&
|
git commit-graph write --stdin-commits <in &&
|
||||||
graph_read_expect 3
|
graph_read_expect 3 generation_data
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'write graph' '
|
test_expect_success 'write graph' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git commit-graph write &&
|
git commit-graph write &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "3"
|
graph_read_expect "3" generation_data
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success POSIXPERM 'write graph has correct permissions' '
|
test_expect_success POSIXPERM 'write graph has correct permissions' '
|
||||||
|
@ -219,7 +219,7 @@ test_expect_success 'write graph with merges' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git commit-graph write &&
|
git commit-graph write &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "10" "extra_edges"
|
graph_read_expect "10" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
|
graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
|
||||||
|
@ -254,7 +254,7 @@ test_expect_success 'write graph with new commit' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git commit-graph write &&
|
git commit-graph write &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "11" "extra_edges"
|
graph_read_expect "11" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -264,7 +264,7 @@ test_expect_success 'write graph with nothing new' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git commit-graph write &&
|
git commit-graph write &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "11" "extra_edges"
|
graph_read_expect "11" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -274,7 +274,7 @@ test_expect_success 'build graph from latest pack with closure' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
cat new-idx | git commit-graph write --stdin-packs &&
|
cat new-idx | git commit-graph write --stdin-packs &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "9" "extra_edges"
|
graph_read_expect "9" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -287,7 +287,7 @@ test_expect_success 'build graph from commits with closure' '
|
||||||
git rev-parse merge/1 >>commits-in &&
|
git rev-parse merge/1 >>commits-in &&
|
||||||
cat commits-in | git commit-graph write --stdin-commits &&
|
cat commits-in | git commit-graph write --stdin-commits &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "6"
|
graph_read_expect "6" "generation_data"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -297,7 +297,7 @@ test_expect_success 'build graph from commits with append' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
|
git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "10" "extra_edges"
|
graph_read_expect "10" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -307,7 +307,7 @@ test_expect_success 'build graph using --reachable' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git commit-graph write --reachable &&
|
git commit-graph write --reachable &&
|
||||||
test_path_is_file $objdir/info/commit-graph &&
|
test_path_is_file $objdir/info/commit-graph &&
|
||||||
graph_read_expect "11" "extra_edges"
|
graph_read_expect "11" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
|
graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
|
||||||
|
@ -328,7 +328,7 @@ test_expect_success 'write graph in bare repo' '
|
||||||
cd "$TRASH_DIRECTORY/bare" &&
|
cd "$TRASH_DIRECTORY/bare" &&
|
||||||
git commit-graph write &&
|
git commit-graph write &&
|
||||||
test_path_is_file $baredir/info/commit-graph &&
|
test_path_is_file $baredir/info/commit-graph &&
|
||||||
graph_read_expect "11" "extra_edges"
|
graph_read_expect "11" "generation_data extra_edges"
|
||||||
'
|
'
|
||||||
|
|
||||||
graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
|
graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
|
||||||
|
@ -454,8 +454,9 @@ test_expect_success 'warn on improper hash version' '
|
||||||
|
|
||||||
test_expect_success 'git commit-graph verify' '
|
test_expect_success 'git commit-graph verify' '
|
||||||
cd "$TRASH_DIRECTORY/full" &&
|
cd "$TRASH_DIRECTORY/full" &&
|
||||||
git rev-parse commits/8 | git commit-graph write --stdin-commits &&
|
git rev-parse commits/8 | GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --stdin-commits &&
|
||||||
git commit-graph verify >output
|
git commit-graph verify >output &&
|
||||||
|
graph_read_expect 9 extra_edges
|
||||||
'
|
'
|
||||||
|
|
||||||
NUM_COMMITS=9
|
NUM_COMMITS=9
|
||||||
|
@ -741,4 +742,56 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# We test the overflow-related code with the following repo history:
|
||||||
|
#
|
||||||
|
# 4:F - 5:N - 6:U
|
||||||
|
# / \
|
||||||
|
# 1:U - 2:N - 3:U M:N
|
||||||
|
# \ /
|
||||||
|
# 7:N - 8:F - 9:N
|
||||||
|
#
|
||||||
|
# Here the commits denoted by U have committer date of zero seconds
|
||||||
|
# since Unix epoch, the commits denoted by N have committer date
|
||||||
|
# starting from 1112354055 seconds since Unix epoch (default committer
|
||||||
|
# date for the test suite), and the commits denoted by F have committer
|
||||||
|
# date of (2 ^ 31 - 2) seconds since Unix epoch.
|
||||||
|
#
|
||||||
|
# The largest offset observed is 2 ^ 31, just large enough to overflow.
|
||||||
|
#
|
||||||
|
|
||||||
|
test_expect_success 'set up and verify repo with generation data overflow chunk' '
|
||||||
|
objdir=".git/objects" &&
|
||||||
|
UNIX_EPOCH_ZERO="@0 +0000" &&
|
||||||
|
FUTURE_DATE="@2147483646 +0000" &&
|
||||||
|
test_oid_cache <<-EOF &&
|
||||||
|
oid_version sha1:1
|
||||||
|
oid_version sha256:2
|
||||||
|
EOF
|
||||||
|
cd "$TRASH_DIRECTORY" &&
|
||||||
|
mkdir repo &&
|
||||||
|
cd repo &&
|
||||||
|
git init &&
|
||||||
|
test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
|
||||||
|
test_commit 2 &&
|
||||||
|
test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
|
||||||
|
git commit-graph write --reachable &&
|
||||||
|
graph_read_expect 3 generation_data &&
|
||||||
|
test_commit --date "$FUTURE_DATE" 4 &&
|
||||||
|
test_commit 5 &&
|
||||||
|
test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
|
||||||
|
git branch left &&
|
||||||
|
git reset --hard 3 &&
|
||||||
|
test_commit 7 &&
|
||||||
|
test_commit --date "$FUTURE_DATE" 8 &&
|
||||||
|
test_commit 9 &&
|
||||||
|
git branch right &&
|
||||||
|
git reset --hard 3 &&
|
||||||
|
test_merge M left right &&
|
||||||
|
git commit-graph write --reachable &&
|
||||||
|
graph_read_expect 10 "generation_data generation_data_overflow" &&
|
||||||
|
git commit-graph verify
|
||||||
|
'
|
||||||
|
|
||||||
|
graph_git_behavior 'generation data overflow chunk repo' repo left right
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -13,11 +13,11 @@ test_expect_success 'setup repo' '
|
||||||
infodir=".git/objects/info" &&
|
infodir=".git/objects/info" &&
|
||||||
graphdir="$infodir/commit-graphs" &&
|
graphdir="$infodir/commit-graphs" &&
|
||||||
test_oid_cache <<-EOM
|
test_oid_cache <<-EOM
|
||||||
shallow sha1:1760
|
shallow sha1:2132
|
||||||
shallow sha256:2064
|
shallow sha256:2436
|
||||||
|
|
||||||
base sha1:1376
|
base sha1:1408
|
||||||
base sha256:1496
|
base sha256:1528
|
||||||
|
|
||||||
oid_version sha1:1
|
oid_version sha1:1
|
||||||
oid_version sha256:2
|
oid_version sha256:2
|
||||||
|
@ -31,9 +31,9 @@ graph_read_expect() {
|
||||||
NUM_BASE=$2
|
NUM_BASE=$2
|
||||||
fi
|
fi
|
||||||
cat >expect <<- EOF
|
cat >expect <<- EOF
|
||||||
header: 43475048 1 $(test_oid oid_version) 3 $NUM_BASE
|
header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
|
||||||
num_commits: $1
|
num_commits: $1
|
||||||
chunks: oid_fanout oid_lookup commit_metadata
|
chunks: oid_fanout oid_lookup commit_metadata generation_data
|
||||||
EOF
|
EOF
|
||||||
test-tool read-graph >output &&
|
test-tool read-graph >output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
|
@ -453,4 +453,185 @@ test_expect_success 'prevent regression for duplicate commits across layers' '
|
||||||
git -C dup commit-graph verify
|
git -C dup commit-graph verify
|
||||||
'
|
'
|
||||||
|
|
||||||
|
NUM_FIRST_LAYER_COMMITS=64
|
||||||
|
NUM_SECOND_LAYER_COMMITS=16
|
||||||
|
NUM_THIRD_LAYER_COMMITS=7
|
||||||
|
NUM_FOURTH_LAYER_COMMITS=8
|
||||||
|
NUM_FIFTH_LAYER_COMMITS=16
|
||||||
|
SECOND_LAYER_SEQUENCE_START=$(($NUM_FIRST_LAYER_COMMITS + 1))
|
||||||
|
SECOND_LAYER_SEQUENCE_END=$(($SECOND_LAYER_SEQUENCE_START + $NUM_SECOND_LAYER_COMMITS - 1))
|
||||||
|
THIRD_LAYER_SEQUENCE_START=$(($SECOND_LAYER_SEQUENCE_END + 1))
|
||||||
|
THIRD_LAYER_SEQUENCE_END=$(($THIRD_LAYER_SEQUENCE_START + $NUM_THIRD_LAYER_COMMITS - 1))
|
||||||
|
FOURTH_LAYER_SEQUENCE_START=$(($THIRD_LAYER_SEQUENCE_END + 1))
|
||||||
|
FOURTH_LAYER_SEQUENCE_END=$(($FOURTH_LAYER_SEQUENCE_START + $NUM_FOURTH_LAYER_COMMITS - 1))
|
||||||
|
FIFTH_LAYER_SEQUENCE_START=$(($FOURTH_LAYER_SEQUENCE_END + 1))
|
||||||
|
FIFTH_LAYER_SEQUENCE_END=$(($FIFTH_LAYER_SEQUENCE_START + $NUM_FIFTH_LAYER_COMMITS - 1))
|
||||||
|
|
||||||
|
# Current split graph chain:
|
||||||
|
#
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
test_expect_success 'setup repo for mixed generation commit-graph-chain' '
|
||||||
|
graphdir=".git/objects/info/commit-graphs" &&
|
||||||
|
test_oid_cache <<-EOF &&
|
||||||
|
oid_version sha1:1
|
||||||
|
oid_version sha256:2
|
||||||
|
EOF
|
||||||
|
git init mixed &&
|
||||||
|
(
|
||||||
|
cd mixed &&
|
||||||
|
git config core.commitGraph true &&
|
||||||
|
git config gc.writeCommitGraph false &&
|
||||||
|
for i in $(test_seq $NUM_FIRST_LAYER_COMMITS)
|
||||||
|
do
|
||||||
|
test_commit $i &&
|
||||||
|
git branch commits/$i || return 1
|
||||||
|
done &&
|
||||||
|
git commit-graph write --reachable --split &&
|
||||||
|
graph_read_expect $NUM_FIRST_LAYER_COMMITS &&
|
||||||
|
test_line_count = 1 $graphdir/commit-graph-chain &&
|
||||||
|
for i in $(test_seq $SECOND_LAYER_SEQUENCE_START $SECOND_LAYER_SEQUENCE_END)
|
||||||
|
do
|
||||||
|
test_commit $i &&
|
||||||
|
git branch commits/$i || return 1
|
||||||
|
done &&
|
||||||
|
GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable --split=no-merge &&
|
||||||
|
test_line_count = 2 $graphdir/commit-graph-chain &&
|
||||||
|
test-tool read-graph >output &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
header: 43475048 1 $(test_oid oid_version) 4 1
|
||||||
|
num_commits: $NUM_SECOND_LAYER_COMMITS
|
||||||
|
chunks: oid_fanout oid_lookup commit_metadata
|
||||||
|
EOF
|
||||||
|
test_cmp expect output &&
|
||||||
|
git commit-graph verify &&
|
||||||
|
cat $graphdir/commit-graph-chain
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# The new layer will be added without generation data chunk as it was not
|
||||||
|
# present on the layer underneath it.
|
||||||
|
#
|
||||||
|
# 7 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
test_expect_success 'do not write generation data chunk if not present on existing tip' '
|
||||||
|
git clone mixed mixed-no-gdat &&
|
||||||
|
(
|
||||||
|
cd mixed-no-gdat &&
|
||||||
|
for i in $(test_seq $THIRD_LAYER_SEQUENCE_START $THIRD_LAYER_SEQUENCE_END)
|
||||||
|
do
|
||||||
|
test_commit $i &&
|
||||||
|
git branch commits/$i || return 1
|
||||||
|
done &&
|
||||||
|
git commit-graph write --reachable --split=no-merge &&
|
||||||
|
test_line_count = 3 $graphdir/commit-graph-chain &&
|
||||||
|
test-tool read-graph >output &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
header: 43475048 1 $(test_oid oid_version) 4 2
|
||||||
|
num_commits: $NUM_THIRD_LAYER_COMMITS
|
||||||
|
chunks: oid_fanout oid_lookup commit_metadata
|
||||||
|
EOF
|
||||||
|
test_cmp expect output &&
|
||||||
|
git commit-graph verify
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Number of commits in each layer of the split-commit graph before merge:
|
||||||
|
#
|
||||||
|
# 8 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 7 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
# The top two layers are merged and do not have generation data chunk as layer below them does
|
||||||
|
# not have generation data chunk.
|
||||||
|
#
|
||||||
|
# 15 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
test_expect_success 'do not write generation data chunk if the topmost remaining layer does not have generation data chunk' '
|
||||||
|
git clone mixed-no-gdat mixed-merge-no-gdat &&
|
||||||
|
(
|
||||||
|
cd mixed-merge-no-gdat &&
|
||||||
|
for i in $(test_seq $FOURTH_LAYER_SEQUENCE_START $FOURTH_LAYER_SEQUENCE_END)
|
||||||
|
do
|
||||||
|
test_commit $i &&
|
||||||
|
git branch commits/$i || return 1
|
||||||
|
done &&
|
||||||
|
git commit-graph write --reachable --split --size-multiple 1 &&
|
||||||
|
test_line_count = 3 $graphdir/commit-graph-chain &&
|
||||||
|
test-tool read-graph >output &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
header: 43475048 1 $(test_oid oid_version) 4 2
|
||||||
|
num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
|
||||||
|
chunks: oid_fanout oid_lookup commit_metadata
|
||||||
|
EOF
|
||||||
|
test_cmp expect output &&
|
||||||
|
git commit-graph verify
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# Number of commits in each layer of the split-commit graph before merge:
|
||||||
|
#
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 15 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 16 commits (No GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
# The top three layers are merged and has generation data chunk as the topmost remaining layer
|
||||||
|
# has generation data chunk.
|
||||||
|
#
|
||||||
|
# 47 commits (GDAT)
|
||||||
|
# ------------------------
|
||||||
|
# 64 commits (GDAT)
|
||||||
|
#
|
||||||
|
test_expect_success 'write generation data chunk if topmost remaining layer has generation data chunk' '
|
||||||
|
git clone mixed-merge-no-gdat mixed-merge-gdat &&
|
||||||
|
(
|
||||||
|
cd mixed-merge-gdat &&
|
||||||
|
for i in $(test_seq $FIFTH_LAYER_SEQUENCE_START $FIFTH_LAYER_SEQUENCE_END)
|
||||||
|
do
|
||||||
|
test_commit $i &&
|
||||||
|
git branch commits/$i || return 1
|
||||||
|
done &&
|
||||||
|
git commit-graph write --reachable --split --size-multiple 1 &&
|
||||||
|
test_line_count = 2 $graphdir/commit-graph-chain &&
|
||||||
|
test-tool read-graph >output &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
header: 43475048 1 $(test_oid oid_version) 5 1
|
||||||
|
num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
|
||||||
|
chunks: oid_fanout oid_lookup commit_metadata generation_data
|
||||||
|
EOF
|
||||||
|
test_cmp expect output
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'write generation data chunk when commit-graph chain is replaced' '
|
||||||
|
git clone mixed mixed-replace &&
|
||||||
|
(
|
||||||
|
cd mixed-replace &&
|
||||||
|
git commit-graph write --reachable --split=replace &&
|
||||||
|
test_path_is_file $graphdir/commit-graph-chain &&
|
||||||
|
test_line_count = 1 $graphdir/commit-graph-chain &&
|
||||||
|
verify_chain_files_exist $graphdir &&
|
||||||
|
graph_read_expect $(($NUM_FIRST_LAYER_COMMITS + $NUM_SECOND_LAYER_COMMITS)) &&
|
||||||
|
git commit-graph verify
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -18,6 +18,8 @@ GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
|
||||||
export GIT_COMMITTER_DATE
|
export GIT_COMMITTER_DATE
|
||||||
|
|
||||||
test_expect_success 'setup tests' '
|
test_expect_success 'setup tests' '
|
||||||
|
GIT_TEST_COMMIT_GRAPH=0 &&
|
||||||
|
export GIT_TEST_COMMIT_GRAPH &&
|
||||||
echo 1 >a1 &&
|
echo 1 >a1 &&
|
||||||
git add a1 &&
|
git add a1 &&
|
||||||
GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
|
GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
|
||||||
|
@ -69,7 +71,7 @@ test_expect_success 'setup tests' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'combined merge conflicts' '
|
test_expect_success 'combined merge conflicts' '
|
||||||
test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git merge -m final G
|
test_must_fail git merge -m final G
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'result contains a conflict' '
|
test_expect_success 'result contains a conflict' '
|
||||||
|
@ -85,6 +87,7 @@ test_expect_success 'result contains a conflict' '
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'virtual trees were processed' '
|
test_expect_success 'virtual trees were processed' '
|
||||||
|
# TODO: fragile test, relies on ambigious merge-base resolution
|
||||||
git ls-files --stage >out &&
|
git ls-files --stage >out &&
|
||||||
|
|
||||||
cat >expect <<-EOF &&
|
cat >expect <<-EOF &&
|
||||||
|
|
|
@ -55,10 +55,13 @@ test_expect_success 'setup' '
|
||||||
git show-ref -s commit-5-5 | git commit-graph write --stdin-commits &&
|
git show-ref -s commit-5-5 | git commit-graph write --stdin-commits &&
|
||||||
mv .git/objects/info/commit-graph commit-graph-half &&
|
mv .git/objects/info/commit-graph commit-graph-half &&
|
||||||
chmod u+w commit-graph-half &&
|
chmod u+w commit-graph-half &&
|
||||||
|
GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable &&
|
||||||
|
mv .git/objects/info/commit-graph commit-graph-no-gdat &&
|
||||||
|
chmod u+w commit-graph-no-gdat &&
|
||||||
git config core.commitGraph true
|
git config core.commitGraph true
|
||||||
'
|
'
|
||||||
|
|
||||||
run_three_modes () {
|
run_all_modes () {
|
||||||
test_when_finished rm -rf .git/objects/info/commit-graph &&
|
test_when_finished rm -rf .git/objects/info/commit-graph &&
|
||||||
"$@" <input >actual &&
|
"$@" <input >actual &&
|
||||||
test_cmp expect actual &&
|
test_cmp expect actual &&
|
||||||
|
@ -67,11 +70,14 @@ run_three_modes () {
|
||||||
test_cmp expect actual &&
|
test_cmp expect actual &&
|
||||||
cp commit-graph-half .git/objects/info/commit-graph &&
|
cp commit-graph-half .git/objects/info/commit-graph &&
|
||||||
"$@" <input >actual &&
|
"$@" <input >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
cp commit-graph-no-gdat .git/objects/info/commit-graph &&
|
||||||
|
"$@" <input >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
}
|
}
|
||||||
|
|
||||||
test_three_modes () {
|
test_all_modes () {
|
||||||
run_three_modes test-tool reach "$@"
|
run_all_modes test-tool reach "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_expect_success 'ref_newer:miss' '
|
test_expect_success 'ref_newer:miss' '
|
||||||
|
@ -80,7 +86,7 @@ test_expect_success 'ref_newer:miss' '
|
||||||
B:commit-4-9
|
B:commit-4-9
|
||||||
EOF
|
EOF
|
||||||
echo "ref_newer(A,B):0" >expect &&
|
echo "ref_newer(A,B):0" >expect &&
|
||||||
test_three_modes ref_newer
|
test_all_modes ref_newer
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'ref_newer:hit' '
|
test_expect_success 'ref_newer:hit' '
|
||||||
|
@ -89,7 +95,7 @@ test_expect_success 'ref_newer:hit' '
|
||||||
B:commit-2-3
|
B:commit-2-3
|
||||||
EOF
|
EOF
|
||||||
echo "ref_newer(A,B):1" >expect &&
|
echo "ref_newer(A,B):1" >expect &&
|
||||||
test_three_modes ref_newer
|
test_all_modes ref_newer
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'in_merge_bases:hit' '
|
test_expect_success 'in_merge_bases:hit' '
|
||||||
|
@ -98,7 +104,7 @@ test_expect_success 'in_merge_bases:hit' '
|
||||||
B:commit-8-8
|
B:commit-8-8
|
||||||
EOF
|
EOF
|
||||||
echo "in_merge_bases(A,B):1" >expect &&
|
echo "in_merge_bases(A,B):1" >expect &&
|
||||||
test_three_modes in_merge_bases
|
test_all_modes in_merge_bases
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'in_merge_bases:miss' '
|
test_expect_success 'in_merge_bases:miss' '
|
||||||
|
@ -107,7 +113,7 @@ test_expect_success 'in_merge_bases:miss' '
|
||||||
B:commit-5-9
|
B:commit-5-9
|
||||||
EOF
|
EOF
|
||||||
echo "in_merge_bases(A,B):0" >expect &&
|
echo "in_merge_bases(A,B):0" >expect &&
|
||||||
test_three_modes in_merge_bases
|
test_all_modes in_merge_bases
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'in_merge_bases_many:hit' '
|
test_expect_success 'in_merge_bases_many:hit' '
|
||||||
|
@ -117,7 +123,7 @@ test_expect_success 'in_merge_bases_many:hit' '
|
||||||
X:commit-5-7
|
X:commit-5-7
|
||||||
EOF
|
EOF
|
||||||
echo "in_merge_bases_many(A,X):1" >expect &&
|
echo "in_merge_bases_many(A,X):1" >expect &&
|
||||||
test_three_modes in_merge_bases_many
|
test_all_modes in_merge_bases_many
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'in_merge_bases_many:miss' '
|
test_expect_success 'in_merge_bases_many:miss' '
|
||||||
|
@ -127,7 +133,7 @@ test_expect_success 'in_merge_bases_many:miss' '
|
||||||
X:commit-8-6
|
X:commit-8-6
|
||||||
EOF
|
EOF
|
||||||
echo "in_merge_bases_many(A,X):0" >expect &&
|
echo "in_merge_bases_many(A,X):0" >expect &&
|
||||||
test_three_modes in_merge_bases_many
|
test_all_modes in_merge_bases_many
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'in_merge_bases_many:miss-heuristic' '
|
test_expect_success 'in_merge_bases_many:miss-heuristic' '
|
||||||
|
@ -137,7 +143,7 @@ test_expect_success 'in_merge_bases_many:miss-heuristic' '
|
||||||
X:commit-6-6
|
X:commit-6-6
|
||||||
EOF
|
EOF
|
||||||
echo "in_merge_bases_many(A,X):0" >expect &&
|
echo "in_merge_bases_many(A,X):0" >expect &&
|
||||||
test_three_modes in_merge_bases_many
|
test_all_modes in_merge_bases_many
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'is_descendant_of:hit' '
|
test_expect_success 'is_descendant_of:hit' '
|
||||||
|
@ -148,7 +154,7 @@ test_expect_success 'is_descendant_of:hit' '
|
||||||
X:commit-1-1
|
X:commit-1-1
|
||||||
EOF
|
EOF
|
||||||
echo "is_descendant_of(A,X):1" >expect &&
|
echo "is_descendant_of(A,X):1" >expect &&
|
||||||
test_three_modes is_descendant_of
|
test_all_modes is_descendant_of
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'is_descendant_of:miss' '
|
test_expect_success 'is_descendant_of:miss' '
|
||||||
|
@ -159,7 +165,7 @@ test_expect_success 'is_descendant_of:miss' '
|
||||||
X:commit-7-6
|
X:commit-7-6
|
||||||
EOF
|
EOF
|
||||||
echo "is_descendant_of(A,X):0" >expect &&
|
echo "is_descendant_of(A,X):0" >expect &&
|
||||||
test_three_modes is_descendant_of
|
test_all_modes is_descendant_of
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'get_merge_bases_many' '
|
test_expect_success 'get_merge_bases_many' '
|
||||||
|
@ -174,7 +180,7 @@ test_expect_success 'get_merge_bases_many' '
|
||||||
git rev-parse commit-5-6 \
|
git rev-parse commit-5-6 \
|
||||||
commit-4-7 | sort
|
commit-4-7 | sort
|
||||||
} >expect &&
|
} >expect &&
|
||||||
test_three_modes get_merge_bases_many
|
test_all_modes get_merge_bases_many
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'reduce_heads' '
|
test_expect_success 'reduce_heads' '
|
||||||
|
@ -196,7 +202,7 @@ test_expect_success 'reduce_heads' '
|
||||||
commit-2-8 \
|
commit-2-8 \
|
||||||
commit-1-10 | sort
|
commit-1-10 | sort
|
||||||
} >expect &&
|
} >expect &&
|
||||||
test_three_modes reduce_heads
|
test_all_modes reduce_heads
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'can_all_from_reach:hit' '
|
test_expect_success 'can_all_from_reach:hit' '
|
||||||
|
@ -219,7 +225,7 @@ test_expect_success 'can_all_from_reach:hit' '
|
||||||
Y:commit-8-1
|
Y:commit-8-1
|
||||||
EOF
|
EOF
|
||||||
echo "can_all_from_reach(X,Y):1" >expect &&
|
echo "can_all_from_reach(X,Y):1" >expect &&
|
||||||
test_three_modes can_all_from_reach
|
test_all_modes can_all_from_reach
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'can_all_from_reach:miss' '
|
test_expect_success 'can_all_from_reach:miss' '
|
||||||
|
@ -241,7 +247,7 @@ test_expect_success 'can_all_from_reach:miss' '
|
||||||
Y:commit-8-5
|
Y:commit-8-5
|
||||||
EOF
|
EOF
|
||||||
echo "can_all_from_reach(X,Y):0" >expect &&
|
echo "can_all_from_reach(X,Y):0" >expect &&
|
||||||
test_three_modes can_all_from_reach
|
test_all_modes can_all_from_reach
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'can_all_from_reach_with_flag: tags case' '
|
test_expect_success 'can_all_from_reach_with_flag: tags case' '
|
||||||
|
@ -264,7 +270,7 @@ test_expect_success 'can_all_from_reach_with_flag: tags case' '
|
||||||
Y:commit-8-1
|
Y:commit-8-1
|
||||||
EOF
|
EOF
|
||||||
echo "can_all_from_reach_with_flag(X,_,_,0,0):1" >expect &&
|
echo "can_all_from_reach_with_flag(X,_,_,0,0):1" >expect &&
|
||||||
test_three_modes can_all_from_reach_with_flag
|
test_all_modes can_all_from_reach_with_flag
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'commit_contains:hit' '
|
test_expect_success 'commit_contains:hit' '
|
||||||
|
@ -280,8 +286,8 @@ test_expect_success 'commit_contains:hit' '
|
||||||
X:commit-9-3
|
X:commit-9-3
|
||||||
EOF
|
EOF
|
||||||
echo "commit_contains(_,A,X,_):1" >expect &&
|
echo "commit_contains(_,A,X,_):1" >expect &&
|
||||||
test_three_modes commit_contains &&
|
test_all_modes commit_contains &&
|
||||||
test_three_modes commit_contains --tag
|
test_all_modes commit_contains --tag
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'commit_contains:miss' '
|
test_expect_success 'commit_contains:miss' '
|
||||||
|
@ -297,8 +303,8 @@ test_expect_success 'commit_contains:miss' '
|
||||||
X:commit-9-3
|
X:commit-9-3
|
||||||
EOF
|
EOF
|
||||||
echo "commit_contains(_,A,X,_):0" >expect &&
|
echo "commit_contains(_,A,X,_):0" >expect &&
|
||||||
test_three_modes commit_contains &&
|
test_all_modes commit_contains &&
|
||||||
test_three_modes commit_contains --tag
|
test_all_modes commit_contains --tag
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: basic topo-order' '
|
test_expect_success 'rev-list: basic topo-order' '
|
||||||
|
@ -310,7 +316,7 @@ test_expect_success 'rev-list: basic topo-order' '
|
||||||
commit-6-2 commit-5-2 commit-4-2 commit-3-2 commit-2-2 commit-1-2 \
|
commit-6-2 commit-5-2 commit-4-2 commit-3-2 commit-2-2 commit-1-2 \
|
||||||
commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
|
commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --topo-order commit-6-6
|
run_all_modes git rev-list --topo-order commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: first-parent topo-order' '
|
test_expect_success 'rev-list: first-parent topo-order' '
|
||||||
|
@ -322,7 +328,7 @@ test_expect_success 'rev-list: first-parent topo-order' '
|
||||||
commit-6-2 \
|
commit-6-2 \
|
||||||
commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
|
commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --first-parent --topo-order commit-6-6
|
run_all_modes git rev-list --first-parent --topo-order commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: range topo-order' '
|
test_expect_success 'rev-list: range topo-order' '
|
||||||
|
@ -334,7 +340,7 @@ test_expect_success 'rev-list: range topo-order' '
|
||||||
commit-6-2 commit-5-2 commit-4-2 \
|
commit-6-2 commit-5-2 commit-4-2 \
|
||||||
commit-6-1 commit-5-1 commit-4-1 \
|
commit-6-1 commit-5-1 commit-4-1 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --topo-order commit-3-3..commit-6-6
|
run_all_modes git rev-list --topo-order commit-3-3..commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: range topo-order' '
|
test_expect_success 'rev-list: range topo-order' '
|
||||||
|
@ -346,7 +352,7 @@ test_expect_success 'rev-list: range topo-order' '
|
||||||
commit-6-2 commit-5-2 commit-4-2 \
|
commit-6-2 commit-5-2 commit-4-2 \
|
||||||
commit-6-1 commit-5-1 commit-4-1 \
|
commit-6-1 commit-5-1 commit-4-1 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --topo-order commit-3-8..commit-6-6
|
run_all_modes git rev-list --topo-order commit-3-8..commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: first-parent range topo-order' '
|
test_expect_success 'rev-list: first-parent range topo-order' '
|
||||||
|
@ -358,7 +364,7 @@ test_expect_success 'rev-list: first-parent range topo-order' '
|
||||||
commit-6-2 \
|
commit-6-2 \
|
||||||
commit-6-1 commit-5-1 commit-4-1 \
|
commit-6-1 commit-5-1 commit-4-1 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
|
run_all_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: ancestry-path topo-order' '
|
test_expect_success 'rev-list: ancestry-path topo-order' '
|
||||||
|
@ -368,7 +374,7 @@ test_expect_success 'rev-list: ancestry-path topo-order' '
|
||||||
commit-6-4 commit-5-4 commit-4-4 commit-3-4 \
|
commit-6-4 commit-5-4 commit-4-4 commit-3-4 \
|
||||||
commit-6-3 commit-5-3 commit-4-3 \
|
commit-6-3 commit-5-3 commit-4-3 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
|
run_all_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rev-list: symmetric difference topo-order' '
|
test_expect_success 'rev-list: symmetric difference topo-order' '
|
||||||
|
@ -382,7 +388,7 @@ test_expect_success 'rev-list: symmetric difference topo-order' '
|
||||||
commit-3-8 commit-2-8 commit-1-8 \
|
commit-3-8 commit-2-8 commit-1-8 \
|
||||||
commit-3-7 commit-2-7 commit-1-7 \
|
commit-3-7 commit-2-7 commit-1-7 \
|
||||||
>expect &&
|
>expect &&
|
||||||
run_three_modes git rev-list --topo-order commit-3-8...commit-6-6
|
run_all_modes git rev-list --topo-order commit-3-8...commit-6-6
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'get_reachable_subset:all' '
|
test_expect_success 'get_reachable_subset:all' '
|
||||||
|
@ -402,7 +408,7 @@ test_expect_success 'get_reachable_subset:all' '
|
||||||
commit-1-7 \
|
commit-1-7 \
|
||||||
commit-5-6 | sort
|
commit-5-6 | sort
|
||||||
) >expect &&
|
) >expect &&
|
||||||
test_three_modes get_reachable_subset
|
test_all_modes get_reachable_subset
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'get_reachable_subset:some' '
|
test_expect_success 'get_reachable_subset:some' '
|
||||||
|
@ -420,7 +426,7 @@ test_expect_success 'get_reachable_subset:some' '
|
||||||
git rev-parse commit-3-3 \
|
git rev-parse commit-3-3 \
|
||||||
commit-1-7 | sort
|
commit-1-7 | sort
|
||||||
) >expect &&
|
) >expect &&
|
||||||
test_three_modes get_reachable_subset
|
test_all_modes get_reachable_subset
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'get_reachable_subset:none' '
|
test_expect_success 'get_reachable_subset:none' '
|
||||||
|
@ -434,7 +440,7 @@ test_expect_success 'get_reachable_subset:none' '
|
||||||
Y:commit-2-8
|
Y:commit-2-8
|
||||||
EOF
|
EOF
|
||||||
echo "get_reachable_subset(X,Y)" >expect &&
|
echo "get_reachable_subset(X,Y)" >expect &&
|
||||||
test_three_modes get_reachable_subset
|
test_all_modes get_reachable_subset
|
||||||
'
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
|
@ -218,6 +218,12 @@ test_commit () {
|
||||||
--signoff)
|
--signoff)
|
||||||
signoff="$1"
|
signoff="$1"
|
||||||
;;
|
;;
|
||||||
|
--date)
|
||||||
|
notick=yes
|
||||||
|
GIT_COMMITTER_DATE="$2"
|
||||||
|
GIT_AUTHOR_DATE="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-C)
|
-C)
|
||||||
indir="$2"
|
indir="$2"
|
||||||
shift
|
shift
|
||||||
|
|
|
@ -493,7 +493,7 @@ static int got_oid(struct upload_pack_data *data,
|
||||||
|
|
||||||
static int ok_to_give_up(struct upload_pack_data *data)
|
static int ok_to_give_up(struct upload_pack_data *data)
|
||||||
{
|
{
|
||||||
uint32_t min_generation = GENERATION_NUMBER_ZERO;
|
timestamp_t min_generation = GENERATION_NUMBER_ZERO;
|
||||||
|
|
||||||
if (!data->have_obj.nr)
|
if (!data->have_obj.nr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче