shallow.c: extend setup_*_shallow() to accept extra shallow commits
[git/git.git] / shallow.c
CommitLineData
ed09aef0
JS
1#include "cache.h"
2#include "commit.h"
abef3a16 3#include "tag.h"
3125fe52 4#include "pkt-line.h"
ed09aef0
JS
5
6static int is_shallow = -1;
6035d6aa
NTND
7static struct stat shallow_stat;
8static char *alternate_shallow_file;
9
10void set_alternate_shallow_file(const char *path)
11{
12 if (is_shallow != -1)
13 die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
14 free(alternate_shallow_file);
15 alternate_shallow_file = path ? xstrdup(path) : NULL;
16}
ed09aef0
JS
17
18int register_shallow(const unsigned char *sha1)
19{
20 struct commit_graft *graft =
21 xmalloc(sizeof(struct commit_graft));
22 struct commit *commit = lookup_commit(sha1);
23
24 hashcpy(graft->sha1, sha1);
25 graft->nr_parent = -1;
26 if (commit && commit->object.parsed)
27 commit->parents = NULL;
28 return register_commit_graft(graft, 0);
29}
30
f43117a6 31int is_repository_shallow(void)
ed09aef0
JS
32{
33 FILE *fp;
34 char buf[1024];
6035d6aa 35 const char *path = alternate_shallow_file;
ed09aef0
JS
36
37 if (is_shallow >= 0)
38 return is_shallow;
39
6035d6aa
NTND
40 if (!path)
41 path = git_path("shallow");
42 /*
43 * fetch-pack sets '--shallow-file ""' as an indicator that no
44 * shallow file should be used. We could just open it and it
45 * will likely fail. But let's do an explicit check instead.
46 */
47 if (!*path ||
48 stat(path, &shallow_stat) ||
49 (fp = fopen(path, "r")) == NULL) {
ed09aef0
JS
50 is_shallow = 0;
51 return is_shallow;
52 }
53 is_shallow = 1;
54
55 while (fgets(buf, sizeof(buf), fp)) {
56 unsigned char sha1[20];
57 if (get_sha1_hex(buf, sha1))
58 die("bad shallow line: %s", buf);
59 register_shallow(sha1);
60 }
61 fclose(fp);
62 return is_shallow;
63}
64
f53514bc
JS
65struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
66 int shallow_flag, int not_shallow_flag)
ed09aef0
JS
67{
68 int i = 0, cur_depth = 0;
69 struct commit_list *result = NULL;
3cd47459 70 struct object_array stack = OBJECT_ARRAY_INIT;
ed09aef0
JS
71 struct commit *commit = NULL;
72
73 while (commit || i < heads->nr || stack.nr) {
74 struct commit_list *p;
75 if (!commit) {
76 if (i < heads->nr) {
77 commit = (struct commit *)
abef3a16 78 deref_tag(heads->objects[i++].item, NULL, 0);
affeef12 79 if (!commit || commit->object.type != OBJ_COMMIT) {
ed09aef0
JS
80 commit = NULL;
81 continue;
82 }
d64d6c9f
AJ
83 if (!commit->util)
84 commit->util = xmalloc(sizeof(int));
85 *(int *)commit->util = 0;
ed09aef0
JS
86 cur_depth = 0;
87 } else {
88 commit = (struct commit *)
89 stack.objects[--stack.nr].item;
90 cur_depth = *(int *)commit->util;
91 }
92 }
dec38c81
MK
93 if (parse_commit(commit))
94 die("invalid commit");
ed09aef0 95 cur_depth++;
682c7d2f
NTND
96 if (cur_depth >= depth) {
97 commit_list_insert(commit, &result);
98 commit->object.flags |= shallow_flag;
99 commit = NULL;
100 continue;
101 }
102 commit->object.flags |= not_shallow_flag;
ed09aef0
JS
103 for (p = commit->parents, commit = NULL; p; p = p->next) {
104 if (!p->item->util) {
105 int *pointer = xmalloc(sizeof(int));
106 p->item->util = pointer;
107 *pointer = cur_depth;
108 } else {
109 int *pointer = p->item->util;
110 if (cur_depth >= *pointer)
111 continue;
112 *pointer = cur_depth;
113 }
4b796951
MK
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;
f53514bc 120 }
ed09aef0
JS
121 }
122 }
123
124 return result;
125}
6035d6aa
NTND
126
127void check_shallow_file_for_update(void)
128{
129 struct stat st;
130
131 if (!is_shallow)
132 return;
133 else if (is_shallow == -1)
134 die("BUG: shallow must be initialized by now");
135
136 if (stat(git_path("shallow"), &st))
137 die("shallow file was removed during fetch");
138 else if (st.st_mtime != shallow_stat.st_mtime
139#ifdef USE_NSEC
140 || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
141#endif
142 )
143 die("shallow file was changed during fetch");
144}
3125fe52
NTND
145
146struct write_shallow_data {
147 struct strbuf *out;
148 int use_pack_protocol;
149 int count;
150};
151
152static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
153{
154 struct write_shallow_data *data = cb_data;
155 const char *hex = sha1_to_hex(graft->sha1);
6a3bbb4d
NTND
156 if (graft->nr_parent != -1)
157 return 0;
3125fe52
NTND
158 data->count++;
159 if (data->use_pack_protocol)
160 packet_buf_write(data->out, "shallow %s", hex);
161 else {
162 strbuf_addstr(data->out, hex);
163 strbuf_addch(data->out, '\n');
164 }
165 return 0;
166}
167
1a30f5a2
NTND
168int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
169 const struct sha1_array *extra)
3125fe52
NTND
170{
171 struct write_shallow_data data;
1a30f5a2 172 int i;
3125fe52
NTND
173 data.out = out;
174 data.use_pack_protocol = use_pack_protocol;
175 data.count = 0;
176 for_each_commit_graft(write_one_shallow, &data);
1a30f5a2
NTND
177 if (!extra)
178 return data.count;
179 for (i = 0; i < extra->nr; i++) {
180 strbuf_addstr(out, sha1_to_hex(extra->sha1[i]));
181 strbuf_addch(out, '\n');
182 data.count++;
183 }
3125fe52
NTND
184 return data.count;
185}
186
1a30f5a2 187char *setup_temporary_shallow(const struct sha1_array *extra)
08ea65ad
NTND
188{
189 struct strbuf sb = STRBUF_INIT;
190 int fd;
191
1a30f5a2 192 if (write_shallow_commits(&sb, 0, extra)) {
08ea65ad
NTND
193 struct strbuf path = STRBUF_INIT;
194 strbuf_addstr(&path, git_path("shallow_XXXXXX"));
195 fd = xmkstemp(path.buf);
196 if (write_in_full(fd, sb.buf, sb.len) != sb.len)
197 die_errno("failed to write to %s",
198 path.buf);
199 close(fd);
200 strbuf_release(&sb);
201 return strbuf_detach(&path, NULL);
202 }
203 /*
204 * is_repository_shallow() sees empty string as "no shallow
205 * file".
206 */
207 return xstrdup("");
208}
209
3125fe52 210void setup_alternate_shallow(struct lock_file *shallow_lock,
1a30f5a2
NTND
211 const char **alternate_shallow_file,
212 const struct sha1_array *extra)
3125fe52
NTND
213{
214 struct strbuf sb = STRBUF_INIT;
215 int fd;
216
217 check_shallow_file_for_update();
218 fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
219 LOCK_DIE_ON_ERROR);
1a30f5a2 220 if (write_shallow_commits(&sb, 0, extra)) {
3125fe52
NTND
221 if (write_in_full(fd, sb.buf, sb.len) != sb.len)
222 die_errno("failed to write to %s",
223 shallow_lock->filename);
224 *alternate_shallow_file = shallow_lock->filename;
225 } else
226 /*
227 * is_repository_shallow() sees empty string as "no
228 * shallow file".
229 */
230 *alternate_shallow_file = "";
231 strbuf_release(&sb);
232}
ad491366
NTND
233
234static int advertise_shallow_grafts_cb(const struct commit_graft *graft, void *cb)
235{
236 int fd = *(int *)cb;
237 if (graft->nr_parent == -1)
238 packet_write(fd, "shallow %s\n", sha1_to_hex(graft->sha1));
239 return 0;
240}
241
242void advertise_shallow_grafts(int fd)
243{
244 if (!is_repository_shallow())
245 return;
246 for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
247}