reachable.c 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
#include "cache.h"
#include "refs.h"
#include "tag.h"
#include "commit.h"
#include "blob.h"
#include "diff.h"
#include "revision.h"
#include "reachable.h"
#include "cache-tree.h"
10
#include "progress.h"
11 12 13 14 15 16 17 18

static void process_blob(struct blob *blob,
			 struct object_array *p,
			 struct name_path *path,
			 const char *name)
{
	struct object *obj = &blob->object;

19 20
	if (!blob)
		die("bad blob object");
21 22 23 24 25 26
	if (obj->flags & SEEN)
		return;
	obj->flags |= SEEN;
	/* Nothing to do, really .. The blob lookup was the important part */
}

27 28 29 30 31 32 33 34
static void process_gitlink(const unsigned char *sha1,
			    struct object_array *p,
			    struct name_path *path,
			    const char *name)
{
	/* I don't think we want to recurse into this, really. */
}

35 36 37 38 39 40 41 42 43 44
static void process_tree(struct tree *tree,
			 struct object_array *p,
			 struct name_path *path,
			 const char *name)
{
	struct object *obj = &tree->object;
	struct tree_desc desc;
	struct name_entry entry;
	struct name_path me;

45 46
	if (!tree)
		die("bad tree object");
47 48 49 50 51 52 53 54 55 56
	if (obj->flags & SEEN)
		return;
	obj->flags |= SEEN;
	if (parse_tree(tree) < 0)
		die("bad tree object %s", sha1_to_hex(obj->sha1));
	add_object(obj, p, path, name);
	me.up = path;
	me.elem = name;
	me.elem_len = strlen(name);

57
	init_tree_desc(&desc, tree->buffer, tree->size);
58 59 60 61

	while (tree_entry(&desc, &entry)) {
		if (S_ISDIR(entry.mode))
			process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
J
Junio C Hamano 已提交
62
		else if (S_ISGITLINK(entry.mode))
63
			process_gitlink(entry.sha1, p, &me, entry.path);
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
		else
			process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
	}
	free(tree->buffer);
	tree->buffer = NULL;
}

static void process_tag(struct tag *tag, struct object_array *p, const char *name)
{
	struct object *obj = &tag->object;

	if (obj->flags & SEEN)
		return;
	obj->flags |= SEEN;

	if (parse_tag(tag) < 0)
		die("bad tag object %s", sha1_to_hex(obj->sha1));
81 82
	if (tag->tagged)
		add_object(tag->tagged, p, NULL, name);
83 84
}

85
static void walk_commit_list(struct rev_info *revs, struct progress *progress)
86 87 88
{
	int i;
	struct commit *commit;
89
	struct object_array objects = OBJECT_ARRAY_INIT;
90
	uint32_t count = 0;
91 92

	/* Walk all commits, process their trees */
93
	while ((commit = get_revision(revs)) != NULL) {
94
		process_tree(commit->tree, &objects, NULL, "");
95 96
		display_progress(progress, ++count);
	}
97 98 99 100 101 102

	/* Then walk all the pending objects, recursively processing them too */
	for (i = 0; i < revs->pending.nr; i++) {
		struct object_array_entry *pending = revs->pending.objects + i;
		struct object *obj = pending->item;
		const char *name = pending->name;
103
		display_progress(progress, ++count);
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
		if (obj->type == OBJ_TAG) {
			process_tag((struct tag *) obj, &objects, name);
			continue;
		}
		if (obj->type == OBJ_TREE) {
			process_tree((struct tree *)obj, &objects, NULL, name);
			continue;
		}
		if (obj->type == OBJ_BLOB) {
			process_blob((struct blob *)obj, &objects, NULL, name);
			continue;
		}
		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
	}
}

120 121 122
static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
		const char *email, unsigned long timestamp, int tz,
		const char *message, void *cb_data)
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
{
	struct object *object;
	struct rev_info *revs = (struct rev_info *)cb_data;

	object = parse_object(osha1);
	if (object)
		add_pending_object(revs, object, "");
	object = parse_object(nsha1);
	if (object)
		add_pending_object(revs, object, "");
	return 0;
}

static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
	struct object *object = parse_object(sha1);
	struct rev_info *revs = (struct rev_info *)cb_data;

	if (!object)
		die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
	add_pending_object(revs, object, "");

	return 0;
}

static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
	for_each_reflog_ent(path, add_one_reflog_ent, cb_data);
	return 0;
}

static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
{
	struct tree *tree = lookup_tree(sha1);
157 158
	if (tree)
		add_pending_object(revs, &tree->object, "");
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
}

static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
{
	int i;

	if (it->entry_count >= 0)
		add_one_tree(it->sha1, revs);
	for (i = 0; i < it->subtree_nr; i++)
		add_cache_tree(it->down[i]->cache_tree, revs);
}

static void add_cache_refs(struct rev_info *revs)
{
	int i;

	read_cache();
	for (i = 0; i < active_nr; i++) {
177 178 179 180 181 182 183
		/*
		 * The index can contain blobs and GITLINKs, GITLINKs are hashes
		 * that don't actually point to objects in the repository, it's
		 * almost guaranteed that they are NOT blobs, so we don't call
		 * lookup_blob() on them, to avoid populating the hash table
		 * with invalid information
		 */
184
		if (S_ISGITLINK(active_cache[i]->ce_mode))
185 186
			continue;

187 188 189 190 191 192 193 194 195 196 197 198
		lookup_blob(active_cache[i]->sha1);
		/*
		 * We could add the blobs to the pending list, but quite
		 * frankly, we don't care. Once we've looked them up, and
		 * added them as objects, we've really done everything
		 * there is to do for a blob
		 */
	}
	if (active_cache_tree)
		add_cache_tree(active_cache_tree, revs);
}

199 200
void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
			    struct progress *progress)
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
{
	/*
	 * Set up revision parsing, and mark us as being interested
	 * in all object types, not just commits.
	 */
	revs->tag_objects = 1;
	revs->blob_objects = 1;
	revs->tree_objects = 1;

	/* Add all refs from the index file */
	add_cache_refs(revs);

	/* Add all external refs */
	for_each_ref(add_one_ref, revs);

216
	/* Add all reflog info */
217
	if (mark_reflog)
218
		for_each_reflog(add_one_reflog, revs);
219 220 221 222 223

	/*
	 * Set up the revision walk - this will move all commits
	 * from the pending list to the commit walking list.
	 */
224 225
	if (prepare_revision_walk(revs))
		die("revision walk setup failed");
226
	walk_commit_list(revs, progress);
227
}