823 lines
22 KiB
C
823 lines
22 KiB
C
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
#include "git-compat-util.h"
|
|
#include "dir.h"
|
|
#include "gettext.h"
|
|
#include "hex.h"
|
|
#include "commit.h"
|
|
#include "diff.h"
|
|
#include "revision.h"
|
|
#include "list-objects-filter.h"
|
|
#include "list-objects-filter-options.h"
|
|
#include "oidmap.h"
|
|
#include "oidset.h"
|
|
#include "object-name.h"
|
|
#include "odb.h"
|
|
|
|
/* Remember to update object flag allocation in object.h */
|
|
/*
|
|
* FILTER_SHOWN_BUT_REVISIT -- we set this bit on tree objects
|
|
* that have been shown, but should be revisited if they appear
|
|
* in the traversal (until we mark it SEEN). This is a way to
|
|
* let us silently de-dup calls to show() in the caller. This
|
|
* is subtly different from the "revision.h:SHOWN" and the
|
|
* "object-name.c:ONELINE_SEEN" bits. And also different from
|
|
* the non-de-dup usage in pack-bitmap.c
|
|
*/
|
|
#define FILTER_SHOWN_BUT_REVISIT (1<<21)
|
|
|
|
struct subfilter {
|
|
struct filter *filter;
|
|
struct oidset seen;
|
|
struct oidset omits;
|
|
struct object_id skip_tree;
|
|
unsigned is_skipping_tree : 1;
|
|
};
|
|
|
|
struct filter {
|
|
enum list_objects_filter_result (*filter_object_fn)(
|
|
struct repository *r,
|
|
enum list_objects_filter_situation filter_situation,
|
|
struct object *obj,
|
|
const char *pathname,
|
|
const char *filename,
|
|
struct oidset *omits,
|
|
void *filter_data);
|
|
|
|
/*
|
|
* Optional. If this function is supplied and the filter needs
|
|
* to collect omits, then this function is called once before
|
|
* free_fn is called.
|
|
*
|
|
* This is required because the following two conditions hold:
|
|
*
|
|
* a. A tree filter can add and remove objects as an object
|
|
* graph is traversed.
|
|
* b. A combine filter's omit set is the union of all its
|
|
* subfilters, which may include tree: filters.
|
|
*
|
|
* As such, the omits sets must be separate sets, and can only
|
|
* be unioned after the traversal is completed.
|
|
*/
|
|
void (*finalize_omits_fn)(struct oidset *omits, void *filter_data);
|
|
|
|
void (*free_fn)(void *filter_data);
|
|
|
|
void *filter_data;
|
|
|
|
/* If non-NULL, the filter collects a list of the omitted OIDs here. */
|
|
struct oidset *omits;
|
|
};
|
|
|
|
static enum list_objects_filter_result filter_blobs_none(
|
|
struct repository *r UNUSED,
|
|
enum list_objects_filter_situation filter_situation,
|
|
struct object *obj,
|
|
const char *pathname UNUSED,
|
|
const char *filename UNUSED,
|
|
struct oidset *omits,
|
|
void *filter_data_ UNUSED)
|
|
{
|
|
switch (filter_situation) {
|
|
default:
|
|
BUG("unknown filter_situation: %d", filter_situation);
|
|
|
|
case LOFS_TAG:
|
|
assert(obj->type == OBJ_TAG);
|
|
/* always include all tag objects */
|
|
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
|
|
|
|
case LOFS_COMMIT:
|
|
assert(obj->type == OBJ_COMMIT);
|
|
/* always include all commit objects */
|
|
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
|
|
|
|
case LOFS_BEGIN_TREE:
|
|
assert(obj->type == OBJ_TREE);
|
|
/* always include all tree objects */
|
|
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
|
|
|
|
case LOFS_END_TREE:
|
|
assert(obj->type == OBJ_TREE);
|
|
return LOFR_ZERO;
|
|
|
|
case LOFS_BLOB:
|
|
assert(obj->type == OBJ_BLOB);
|
|
assert((obj->flags & SEEN) == 0);
|
|
|
|
if (omits)
|
|
oidset_insert(omits, &obj->oid);
|
|
return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */
|
|
}
|
|
}
|
|
|
|
static void filter_blobs_none__init(
|
|
struct list_objects_filter_options *filter_options UNUSED,
|
|
struct filter *filter)
|
|
{
|
|
filter->filter_object_fn = filter_blobs_none;
|
|
filter->free_fn = free;
|
|
}
|
|
|
|
/*
|
|
* A filter for list-objects to omit ALL trees and blobs from the traversal.
|
|
* Can OPTIONALLY collect a list of the omitted OIDs.
|
|
*/
|
|
struct filter_trees_depth_data {
|
|
/*
|
|
* Maps trees to the minimum depth at which they were seen. It is not
|
|
* necessary to re-traverse a tree at deeper or equal depths than it has
|
|
* already been traversed.
|
|
*
|
|
* We can't use LOFR_MARK_SEEN for tree objects since this will prevent
|
|
* it from being traversed at shallower depths.
|
|
*/
|
|
struct oidmap seen_at_depth;
|
|
|
|
unsigned long exclude_depth;
|
|
unsigned long current_depth;
|
|
};
|
|
|
|
struct seen_map_entry {
|
|
struct oidmap_entry base;
|
|
size_t depth;
|
|
};
|
|
|
|
/* Returns 1 if the oid was in the omits set before it was invoked. */
|
|
static int filter_trees_update_omits(
|
|
struct object *obj,
|
|
struct oidset *omits,
|
|
int include_it)
|
|
{
|
|
if (!omits)
|
|
return 0;
|
|
|
|
if (include_it)
|
|
return oidset_remove(omits, &obj->oid);
|
|
else
|
|
return oidset_insert(omits, &obj->oid);
|
|
}
|
|
|
|
static enum list_objects_filter_result filter_trees_depth(
|
|
struct repository *r UNUSED,
|
|
enum list_objects_filter_situation filter_situation,
|
|
struct object *obj,
|
|
const char *pathname UNUSED,
|
|
const char *filename UNUSED,
|
|
struct oidset *omits,
|
|
void *filter_data_)
|
|
{
|
|
struct filter_trees_depth_data *filter_data = filter_data_;
|
|
struct seen_map_entry *seen_info;
|
|
int include_it = filter_data->current_depth <
|
|
filter_data->exclude_depth;
|
|
int filter_res;
|
|
int already_seen;
|
|
|
|
/*
|
|
* Note that we do not use _MARK_SEEN in order to allow re-traversal in
|
|
* case we encounter a tree or blob again at a shallower depth.
|
|
*/
|
|
|
|
switch (filter_situation) {
|
|
default:
|
|
BUG("unknown filter_situation: %d", filter_situation);
|
|
|
|
case LOFS_TAG:
|
|
assert(obj->type == OBJ_TAG);
|
|
/* always include all tag objects */
|
|
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
|
|
|
|
case LOFS_COMMIT:
|
|
assert(obj->type == OBJ_COMMIT);
|
|
/* always include all commit objects */
|
|
return LOFR_MARK_SEEN | LOFR_DO_SHOW;
|
|
|
|
case LOFS_END_TREE:
|
|
assert(obj->type == OBJ_TREE);
|
|
filter_data->current_depth--;
|
|
return LOFR_ZERO;
|
|
|
|
case LOFS_BLOB:
|
|
filter_trees_update_omits(obj, omits, include_it);
|
|
return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO;
|
|
|
|
case LOFS_BEGIN_TREE:
|
|
seen_info = oidmap_get(
|
|
&filter_data->seen_at_depth, &obj->oid);
|
|
if (!seen_info) {
|
|
CALLOC_ARRAY(seen_info, 1);
|
|
oidcpy(&seen_info->base.oid, &obj->oid);
|
|
seen_info->depth = filter_data->current_depth;
|
|
oidmap_put(&filter_data->seen_at_depth, seen_info);
|
|
already_seen = 0;
|
|
} else {
|
|
already_seen =
|
|
filter_data->current_depth >= seen_info->depth;
|
|
}
|
|
|
|
if (already_seen) {
|
|
filter_res = LOFR_SKIP_TREE;
|
|
} else {
|
|
int been_omitted = filter_trees_update_omits(
|
|
obj, omits, include_it);
|
|
seen_info->depth = filter_data->current_depth;
|
|
|
|
if (include_it)
|
|
filter_res = LOFR_DO_SHOW;
|
|
else if (omits && !been_omitted)
|
|
/*
|
|
* Must update omit information of children
|
|
* recursively; they have not been omitted yet.
|
|
*/
|
|
filter_res = LOFR_ZERO;
|
|
else
|
|
filter_res = LOFR_SKIP_TREE;
|
|
}
|
|
|
|
filter_data->current_depth++;
|
|
return filter_res;
|
|
}
|
|
}
|
|
|
|
static void filter_trees_free(void *filter_data) {
|
|
struct filter_trees_depth_data *d = filter_data;
|
|
if (!d)
|
|
return;
|
|
oidmap_clear(&d->seen_at_depth, 1);
|
|
free(d);
|
|
}
|
|
|
|
static void filter_trees_depth__init(
|
|
struct list_objects_filter_options *filter_options,
|
|
struct filter *filter)
|
|
{
|
|
struct filter_trees_depth_data *d = xcalloc(1, sizeof( |