shallow.c 4.7 KB
Newer Older
1 2
#include "cache.h"
#include "commit.h"
3
#include "tag.h"
4
#include "pkt-line.h"
5 6

static int is_shallow = -1;
7 8 9 10 11 12 13 14 15 16
static struct stat shallow_stat;
static char *alternate_shallow_file;

void set_alternate_shallow_file(const char *path)
{
	if (is_shallow != -1)
		die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
	free(alternate_shallow_file);
	alternate_shallow_file = path ? xstrdup(path) : NULL;
}
17 18 19 20 21 22 23 24 25 26 27 28 29 30

int register_shallow(const unsigned char *sha1)
{
	struct commit_graft *graft =
		xmalloc(sizeof(struct commit_graft));
	struct commit *commit = lookup_commit(sha1);

	hashcpy(graft->sha1, sha1);
	graft->nr_parent = -1;
	if (commit && commit->object.parsed)
		commit->parents = NULL;
	return register_commit_graft(graft, 0);
}

31
int is_repository_shallow(void)
32 33 34
{
	FILE *fp;
	char buf[1024];
35
	const char *path = alternate_shallow_file;
36 37 38 39

	if (is_shallow >= 0)
		return is_shallow;

40 41 42 43 44 45 46 47 48 49
	if (!path)
		path = git_path("shallow");
	/*
	 * fetch-pack sets '--shallow-file ""' as an indicator that no
	 * shallow file should be used. We could just open it and it
	 * will likely fail. But let's do an explicit check instead.
	 */
	if (!*path ||
	    stat(path, &shallow_stat) ||
	    (fp = fopen(path, "r")) == NULL) {
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
		is_shallow = 0;
		return is_shallow;
	}
	is_shallow = 1;

	while (fgets(buf, sizeof(buf), fp)) {
		unsigned char sha1[20];
		if (get_sha1_hex(buf, sha1))
			die("bad shallow line: %s", buf);
		register_shallow(sha1);
	}
	fclose(fp);
	return is_shallow;
}

65 66
struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
		int shallow_flag, int not_shallow_flag)
67 68 69
{
	int i = 0, cur_depth = 0;
	struct commit_list *result = NULL;
70
	struct object_array stack = OBJECT_ARRAY_INIT;
71 72 73 74 75 76 77
	struct commit *commit = NULL;

	while (commit || i < heads->nr || stack.nr) {
		struct commit_list *p;
		if (!commit) {
			if (i < heads->nr) {
				commit = (struct commit *)
78
					deref_tag(heads->objects[i++].item, NULL, 0);
79
				if (!commit || commit->object.type != OBJ_COMMIT) {
80 81 82
					commit = NULL;
					continue;
				}
83 84 85
				if (!commit->util)
					commit->util = xmalloc(sizeof(int));
				*(int *)commit->util = 0;
86 87 88 89 90 91 92
				cur_depth = 0;
			} else {
				commit = (struct commit *)
					stack.objects[--stack.nr].item;
				cur_depth = *(int *)commit->util;
			}
		}
93 94
		if (parse_commit(commit))
			die("invalid commit");
95
		cur_depth++;
96 97 98 99 100 101 102
		if (cur_depth >= depth) {
			commit_list_insert(commit, &result);
			commit->object.flags |= shallow_flag;
			commit = NULL;
			continue;
		}
		commit->object.flags |= not_shallow_flag;
103 104 105 106 107 108 109 110 111 112 113
		for (p = commit->parents, commit = NULL; p; p = p->next) {
			if (!p->item->util) {
				int *pointer = xmalloc(sizeof(int));
				p->item->util = pointer;
				*pointer =  cur_depth;
			} else {
				int *pointer = p->item->util;
				if (cur_depth >= *pointer)
					continue;
				*pointer = cur_depth;
			}
114 115 116 117 118 119
			if (p->next)
				add_object_array(&p->item->object,
						NULL, &stack);
			else {
				commit = p->item;
				cur_depth = *(int *)commit->util;
120
			}
121 122 123 124 125
		}
	}

	return result;
}
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

void check_shallow_file_for_update(void)
{
	struct stat st;

	if (!is_shallow)
		return;
	else if (is_shallow == -1)
		die("BUG: shallow must be initialized by now");

	if (stat(git_path("shallow"), &st))
		die("shallow file was removed during fetch");
	else if (st.st_mtime != shallow_stat.st_mtime
#ifdef USE_NSEC
		 || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
#endif
		   )
		die("shallow file was changed during fetch");
}
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

struct write_shallow_data {
	struct strbuf *out;
	int use_pack_protocol;
	int count;
};

static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
{
	struct write_shallow_data *data = cb_data;
	const char *hex = sha1_to_hex(graft->sha1);
	data->count++;
	if (data->use_pack_protocol)
		packet_buf_write(data->out, "shallow %s", hex);
	else {
		strbuf_addstr(data->out, hex);
		strbuf_addch(data->out, '\n');
	}
	return 0;
}

int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
{
	struct write_shallow_data data;
	data.out = out;
	data.use_pack_protocol = use_pack_protocol;
	data.count = 0;
	for_each_commit_graft(write_one_shallow, &data);
	return data.count;
}

void setup_alternate_shallow(struct lock_file *shallow_lock,
			     const char **alternate_shallow_file)
{
	struct strbuf sb = STRBUF_INIT;
	int fd;

	check_shallow_file_for_update();
	fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
				       LOCK_DIE_ON_ERROR);
	if (write_shallow_commits(&sb, 0)) {
		if (write_in_full(fd, sb.buf, sb.len) != sb.len)
			die_errno("failed to write to %s",
				  shallow_lock->filename);
		*alternate_shallow_file = shallow_lock->filename;
	} else
		/*
		 * is_repository_shallow() sees empty string as "no
		 * shallow file".
		 */
		*alternate_shallow_file = "";
	strbuf_release(&sb);
}