1#include "cache.h"
2#include "commit.h"
3#include "tag.h"
4
5static int is_shallow = -1;
6static struct stat shallow_stat;
7static char *alternate_shallow_file;
8
9void set_alternate_shallow_file(const char *path)
10{
11 if (is_shallow != -1)
12 die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
13 free(alternate_shallow_file);
14 alternate_shallow_file = path ? xstrdup(path) : NULL;
15}
16
17int register_shallow(const unsigned char *sha1)
18{
19 struct commit_graft *graft =
20 xmalloc(sizeof(struct commit_graft));
21 struct commit *commit = lookup_commit(sha1);
22
23 hashcpy(graft->sha1, sha1);
24 graft->nr_parent = -1;
25 if (commit && commit->object.parsed)
26 commit->parents = NULL;
27 return register_commit_graft(graft, 0);
28}
29
30int is_repository_shallow(void)
31{
32 FILE *fp;
33 char buf[1024];
34 const char *path = alternate_shallow_file;
35
36 if (is_shallow >= 0)
37 return is_shallow;
38
39 if (!path)
40 path = git_path("shallow");
41 /*
42 * fetch-pack sets '--shallow-file ""' as an indicator that no
43 * shallow file should be used. We could just open it and it
44 * will likely fail. But let's do an explicit check instead.
45 */
46 if (!*path ||
47 stat(path, &shallow_stat) ||
48 (fp = fopen(path, "r")) == NULL) {
49 is_shallow = 0;
50 return is_shallow;
51 }
52 is_shallow = 1;
53
54 while (fgets(buf, sizeof(buf), fp)) {
55 unsigned char sha1[20];
56 if (get_sha1_hex(buf, sha1))
57 die("bad shallow line: %s", buf);
58 register_shallow(sha1);
59 }
60 fclose(fp);
61 return is_shallow;
62}
63
64struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
65 int shallow_flag, int not_shallow_flag)
66{
67 int i = 0, cur_depth = 0;
68 struct commit_list *result = NULL;
69 struct object_array stack = OBJECT_ARRAY_INIT;
70 struct commit *commit = NULL;
71
72 while (commit || i < heads->nr || stack.nr) {
73 struct commit_list *p;
74 if (!commit) {
75 if (i < heads->nr) {
76 commit = (struct commit *)
77 deref_tag(heads->objects[i++].item, NULL, 0);
78 if (!commit || commit->object.type != OBJ_COMMIT) {
79 commit = NULL;
80 continue;
81 }
82 if (!commit->util)
83 commit->util = xmalloc(sizeof(int));
84 *(int *)commit->util = 0;
85 cur_depth = 0;
86 } else {
87 commit = (struct commit *)
88 stack.objects[--stack.nr].item;
89 cur_depth = *(int *)commit->util;
90 }
91 }
92 if (parse_commit(commit))
93 die("invalid commit");
94 cur_depth++;
95 if (cur_depth >= depth) {
96 commit_list_insert(commit, &result);
97 commit->object.flags |= shallow_flag;
98 commit = NULL;
99 continue;
100 }
101 commit->object.flags |= not_shallow_flag;
102 for (p = commit->parents, commit = NULL; p; p = p->next) {
103 if (!p->item->util) {
104 int *pointer = xmalloc(sizeof(int));
105 p->item->util = pointer;
106 *pointer = cur_depth;
107 } else {
108 int *pointer = p->item->util;
109 if (cur_depth >= *pointer)
110 continue;
111 *pointer = cur_depth;
112 }
113 if (cur_depth < depth) {
114 if (p->next)
115 add_object_array(&p->item->object,
116 NULL, &stack);
117 else {
118 commit = p->item;
119 cur_depth = *(int *)commit->util;
120 }
121 } else {
122 commit_list_insert(p->item, &result);
123 p->item->object.flags |= shallow_flag;
124 }
125 }
126 }
127
128 return result;
129}
130
131void check_shallow_file_for_update(void)
132{
133 struct stat st;
134
135 if (!is_shallow)
136 return;
137 else if (is_shallow == -1)
138 die("BUG: shallow must be initialized by now");
139
140 if (stat(git_path("shallow"), &st))
141 die("shallow file was removed during fetch");
142 else if (st.st_mtime != shallow_stat.st_mtime
143#ifdef USE_NSEC
144 || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
145#endif
146 )
147 die("shallow file was changed during fetch");
148}