Merge branch 'jk/t5531-prepare-to-default-to-non-matching'
[git] / shallow.c
1 #include "cache.h"
2 #include "commit.h"
3 #include "tag.h"
4 #include "pkt-line.h"
5
6 static int is_shallow = -1;
7 static struct stat shallow_stat;
8 static char *alternate_shallow_file;
9
10 void 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 }
17
18 int 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
31 int is_repository_shallow(void)
32 {
33         FILE *fp;
34         char buf[1024];
35         const char *path = alternate_shallow_file;
36
37         if (is_shallow >= 0)
38                 return is_shallow;
39
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) {
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
65 struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
66                 int shallow_flag, int not_shallow_flag)
67 {
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;
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 *)
78                                         deref_tag(heads->objects[i++].item, NULL, 0);
79                                 if (!commit || commit->object.type != OBJ_COMMIT) {
80                                         commit = NULL;
81                                         continue;
82                                 }
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;
91                         }
92                 }
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;
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 (p->next)
114                                 add_object_array(&p->item->object,
115                                                 NULL, &stack);
116                         else {
117                                 commit = p->item;
118                                 cur_depth = *(int *)commit->util;
119                         }
120                 }
121         }
122
123         return result;
124 }
125
126 void check_shallow_file_for_update(void)
127 {
128         struct stat st;
129
130         if (!is_shallow)
131                 return;
132         else if (is_shallow == -1)
133                 die("BUG: shallow must be initialized by now");
134
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
141                    )
142                 die("shallow file was changed during fetch");
143 }
144
145 struct write_shallow_data {
146         struct strbuf *out;
147         int use_pack_protocol;
148         int count;
149 };
150
151 static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
152 {
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');
163         }
164         return 0;
165 }
166
167 int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
168 {
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;
175 }
176
177 char *setup_temporary_shallow(void)
178 {
179         struct strbuf sb = STRBUF_INIT;
180         int fd;
181
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);
192         }
193         /*
194          * is_repository_shallow() sees empty string as "no shallow
195          * file".
196          */
197         return xstrdup("");
198 }
199
200 void setup_alternate_shallow(struct lock_file *shallow_lock,
201                              const char **alternate_shallow_file)
202 {
203         struct strbuf sb = STRBUF_INIT;
204         int fd;
205
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
215                 /*
216                  * is_repository_shallow() sees empty string as "no
217                  * shallow file".
218                  */
219                 *alternate_shallow_file = "";
220         strbuf_release(&sb);
221 }