summaryrefslogtreecommitdiff
path: root/repack-filtered.c
blob: 869b9fc6e3b94c1fa570816e263cec6eff99e11d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "git-compat-util.h"
#include "repack.h"
#include "repository.h"
#include "run-command.h"
#include "string-list.h"
#include "hex.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
#include "list-objects-filter.h"
#include "odb.h"
#include "promisor-remote.h"

int write_filtered_pack(const struct write_pack_opts *opts,
			struct existing_packs *existing,
			struct string_list *names)
{
	struct child_process cmd = CHILD_PROCESS_INIT;
	struct string_list_item *item;
	FILE *in;
	int ret;
	const char *caret;
	const char *pack_prefix = write_pack_opts_pack_prefix(opts);

	prepare_pack_objects(&cmd, opts->po_args, opts->destination);

	strvec_push(&cmd.args, "--stdin-packs");

	for_each_string_list_item(item, &existing->kept_packs)
		strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);

	cmd.in = -1;

	ret = start_command(&cmd);
	if (ret)
		return ret;

	/*
	 * Here 'names' contains only the pack(s) that were just
	 * written, which is exactly the packs we want to keep. Also
	 * 'existing_kept_packs' already contains the packs in
	 * 'keep_pack_list'.
	 */
	in = xfdopen(cmd.in, "w");
	for_each_string_list_item(item, names)
		fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
	for_each_string_list_item(item, &existing->non_kept_packs)
		fprintf(in, "%s.pack\n", item->string);
	for_each_string_list_item(item, &existing->cruft_packs)
		fprintf(in, "%s.pack\n", item->string);
	caret = opts->po_args->pack_kept_objects ? "" : "^";
	for_each_string_list_item(item, &existing->kept_packs)
		fprintf(in, "%s%s.pack\n", caret, item->string);
	fclose(in);

	return finish_pack_objects_cmd(existing->repo->hash_algo, opts, &cmd,
				       names);
}

struct collect_cb_data {
	struct repository *repo;
	struct oidset *set;
};

static int collect_promisor_blob(const struct object_id *oid,
				 struct object_info *oi UNUSED,
				 void *cb_data)
{
	struct collect_cb_data *data = cb_data;
	struct object_info info = OBJECT_INFO_INIT;
	enum object_type type;

	info.typep = &type;

	/*
	 * Use OBJECT_INFO_SKIP_FETCH_OBJECT to avoid triggering a
	 * lazy fetch while collecting promisor blobs.
	 */
	if (odb_read_object_info_extended(data->repo->objects, oid, &info,
			OBJECT_INFO_SKIP_FETCH_OBJECT) < 0)
		return 0;

	if (type == OBJ_BLOB)
		oidset_insert(data->set, oid);

	return 0;
}

int enumerate_promisor_blobs(struct repository *repo,
			     const struct list_objects_filter_options *filter,
			     struct oidset *to_drop)
{
	struct oidset all_promisor_blobs = OIDSET_INIT;
	struct collect_cb_data cb = {
		.repo = repo,
		.set = &all_promisor_blobs
	};
	int ret = 0;

	/*
	 * The caller (cmd_repack) is responsible for validating that a
	 * blob:limit filter and a promisor remote are present before
	 * calling this function.
	 *
	 * Walk only promisor objects. Every object visited here is a
	 * promisor object, so it is recoverable from the promisor remote
	 * as long as the remote still has it, the same assumption the rest
	 * of partial clone relies on.
	 *
	 * We do not use write_filtered_pack() here because git repack
	 * routes promisor objects through repack_promisor_objects()
	 * before the filter machinery runs, so the filtered pack never
	 * contains promisor blobs. Direct enumeration via
	 * ODB_FOR_EACH_OBJECT_PROMISOR_ONLY is the correct approach.
	 */
	ret = odb_for_each_object(repo->objects, NULL,
			collect_promisor_blob, &cb,
			ODB_FOR_EACH_OBJECT_PROMISOR_ONLY);
	if (ret)
		goto cleanup;

	/*
	 * Apply the filter to find which blobs exceed the threshold.
	 * The caller has to_drop and is responsible for clearing it.
	 */
	ret = list_objects_filter__filter_oidset(repo,
		filter,
		&all_promisor_blobs,
		to_drop);

cleanup:
	oidset_clear(&all_promisor_blobs);
	return ret;
}