git p4 test: is_cli_file_writeable succeeds
[git.git] / shallow.c
blob961cf6f02480e04d1baebb8f508aa17c5fea6355
1 #include "cache.h"
2 #include "commit.h"
3 #include "tag.h"
4 #include "pkt-line.h"
6 static int is_shallow = -1;
7 static struct stat shallow_stat;
8 static char *alternate_shallow_file;
10 void set_alternate_shallow_file(const char *path)
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;
18 int register_shallow(const unsigned char *sha1)
20 struct commit_graft *graft =
21 xmalloc(sizeof(struct commit_graft));
22 struct commit *commit = lookup_commit(sha1);
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);
31 int is_repository_shallow(void)
33 FILE *fp;
34 char buf[1024];
35 const char *path = alternate_shallow_file;
37 if (is_shallow >= 0)
38 return is_shallow;
40 if (!path)
41 path = git_path("shallow");
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.
47 if (!*path ||
48 stat(path, &shallow_stat) ||
49 (fp = fopen(path, "r")) == NULL) {
50 is_shallow = 0;
51 return is_shallow;
53 is_shallow = 1;
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);
61 fclose(fp);
62 return is_shallow;
65 struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
66 int shallow_flag, int not_shallow_flag)
68 int i = 0, cur_depth = 0;
69 struct commit_list *result = NULL;
70 struct object_array stack = OBJECT_ARRAY_INIT;
71 struct commit *commit = NULL;
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 *)
78 deref_tag(heads->objects[i++].item, NULL, 0);
79 if (!commit || commit->object.type != OBJ_COMMIT) {
80 commit = NULL;
81 continue;
83 if (!commit->util)
84 commit->util = xmalloc(sizeof(int));
85 *(int *)commit->util = 0;
86 cur_depth = 0;
87 } else {
88 commit = (struct commit *)
89 stack.objects[--stack.nr].item;
90 cur_depth = *(int *)commit->util;
93 parse_commit_or_die(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;
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;
113 if (p->next)
114 add_object_array(&p->item->object,
115 NULL, &stack);
116 else {
117 commit = p->item;
118 cur_depth = *(int *)commit->util;
123 return result;
126 void check_shallow_file_for_update(void)
128 struct stat st;
130 if (!is_shallow)
131 return;
132 else if (is_shallow == -1)
133 die("BUG: shallow must be initialized by now");
135 if (stat(git_path("shallow"), &st))
136 die("shallow file was removed during fetch");
137 else if (st.st_mtime != shallow_stat.st_mtime
138 #ifdef USE_NSEC
139 || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
140 #endif
142 die("shallow file was changed during fetch");
145 struct write_shallow_data {
146 struct strbuf *out;
147 int use_pack_protocol;
148 int count;
151 static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
153 struct write_shallow_data *data = cb_data;
154 const char *hex = sha1_to_hex(graft->sha1);
155 if (graft->nr_parent != -1)
156 return 0;
157 data->count++;
158 if (data->use_pack_protocol)
159 packet_buf_write(data->out, "shallow %s", hex);
160 else {
161 strbuf_addstr(data->out, hex);
162 strbuf_addch(data->out, '\n');
164 return 0;
167 int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
169 struct write_shallow_data data;
170 data.out = out;
171 data.use_pack_protocol = use_pack_protocol;
172 data.count = 0;
173 for_each_commit_graft(write_one_shallow, &data);
174 return data.count;
177 char *setup_temporary_shallow(void)
179 struct strbuf sb = STRBUF_INIT;
180 int fd;
182 if (write_shallow_commits(&sb, 0)) {
183 struct strbuf path = STRBUF_INIT;
184 strbuf_addstr(&path, git_path("shallow_XXXXXX"));
185 fd = xmkstemp(path.buf);
186 if (write_in_full(fd, sb.buf, sb.len) != sb.len)
187 die_errno("failed to write to %s",
188 path.buf);
189 close(fd);
190 strbuf_release(&sb);
191 return strbuf_detach(&path, NULL);
194 * is_repository_shallow() sees empty string as "no shallow
195 * file".
197 return xstrdup("");
200 void setup_alternate_shallow(struct lock_file *shallow_lock,
201 const char **alternate_shallow_file)
203 struct strbuf sb = STRBUF_INIT;
204 int fd;
206 check_shallow_file_for_update();
207 fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
208 LOCK_DIE_ON_ERROR);
209 if (write_shallow_commits(&sb, 0)) {
210 if (write_in_full(fd, sb.buf, sb.len) != sb.len)
211 die_errno("failed to write to %s",
212 shallow_lock->filename);
213 *alternate_shallow_file = shallow_lock->filename;
214 } else
216 * is_repository_shallow() sees empty string as "no
217 * shallow file".
219 *alternate_shallow_file = "";
220 strbuf_release(&sb);