1
+#include "cache.h"
2
+#include "submodule-config.h"
3
+#include "submodule.h"
4
+#include "strbuf.h"
5
+
6
+/*
7
+ * submodule cache lookup structure
8
+ * There is one shared set of 'struct submodule' entries which can be
9
+ * looked up by their sha1 blob id of the .gitmodule file and either
10
+ * using path or name as key.
11
+ * for_path stores submodule entries with path as key
12
+ * for_name stores submodule entries with name as key
13
+ */
14
+struct submodule_cache {
15
+ struct hashmap for_path;
16
+ struct hashmap for_name;
17
+};
18
+
19
+/*
20
+ * thin wrapper struct needed to insert 'struct submodule' entries to
21
+ * the hashmap
22
+ */
23
+struct submodule_entry {
24
+ struct hashmap_entry ent;
25
+ struct submodule *config;
26
+};
27
+
28
+enum lookup_type {
29
+ lookup_name,
30
+ lookup_path
31
+};
32
+
33
+static struct submodule_cache cache;
34
+static int is_cache_init;
35
+
36
+static int config_path_cmp(const struct submodule_entry *a,
37
+ const struct submodule_entry *b,
38
+ const void *unused)
39
+{
40
+ return strcmp(a->config->path, b->config->path) ||
41
+ hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
42
+}
43
+
44
+static int config_name_cmp(const struct submodule_entry *a,
45
+ const struct submodule_entry *b,
46
+ const void *unused)
47
+{
48
+ return strcmp(a->config->name, b->config->name) ||
49
+ hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
50
+}
51
+
52
+static void cache_init(struct submodule_cache *cache)
53
+{
54
+ hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0);
55
+ hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0);
56
+}
57
+
58
+static void free_one_config(struct submodule_entry *entry)
59
+{
60
+ free((void *) entry->config->path);
61
+ free((void *) entry->config->name);
62
+ free(entry->config);
63
+}
64
+
65
+static void cache_free(struct submodule_cache *cache)
66
+{
67
+ struct hashmap_iter iter;
68
+ struct submodule_entry *entry;
69
+
70
+ /*
71
+ * We iterate over the name hash here to be symmetric with the
72
+ * allocation of struct submodule entries. Each is allocated by
73
+ * their .gitmodule blob sha1 and submodule name.
74
+ */
75
+ hashmap_iter_init(&cache->for_name, &iter);
76
+ while ((entry = hashmap_iter_next(&iter)))
77
+ free_one_config(entry);
78
+
79
+ hashmap_free(&cache->for_path, 1);
80
+ hashmap_free(&cache->for_name, 1);
81
+}
82
+
83
+static unsigned int hash_sha1_string(const unsigned char *sha1,
84
+ const char *string)
85
+{
86
+ return memhash(sha1, 20) + strhash(string);
87
+}
88
+
89
+static void cache_put_path(struct submodule_cache *cache,
90
+ struct submodule *submodule)
91
+{
92
+ unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
93
+ submodule->path);
94
+ struct submodule_entry *e = xmalloc(sizeof(*e));
95
+ hashmap_entry_init(e, hash);
96
+ e->config = submodule;
97
+ hashmap_put(&cache->for_path, e);
98
+}
99
+
100
+static void cache_remove_path(struct submodule_cache *cache,
101
+ struct submodule *submodule)
102
+{
103
+ unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
104
+ submodule->path);
105
+ struct submodule_entry e;
106
+ struct submodule_entry *removed;
107
+ hashmap_entry_init(&e, hash);
108
+ e.config = submodule;
109
+ removed = hashmap_remove(&cache->for_path, &e, NULL);
110
+ free(removed);
111
+}
112
+
113
+static void cache_add(struct submodule_cache *cache,
114
+ struct submodule *submodule)
115
+{
116
+ unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
117
+ submodule->name);
118
+ struct submodule_entry *e = xmalloc(sizeof(*e));
119
+ hashmap_entry_init(e, hash);
120
+ e->config = submodule;
121
+ hashmap_add(&cache->for_name, e);
122
+}
123
+
124
+static const struct submodule *cache_lookup_path(struct submodule_cache *cache,
125
+ const unsigned char *gitmodules_sha1, const char *path)
126
+{
127
+ struct submodule_entry *entry;
128
+ unsigned int hash = hash_sha1_string(gitmodules_sha1, path);
129
+ struct submodule_entry key;
130
+ struct submodule key_config;
131
+
132
+ hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
133
+ key_config.path = path;
134
+
135
+ hashmap_entry_init(&key, hash);
136
+ key.config = &key_config;
137
+
138
+ entry = hashmap_get(&cache->for_path, &key, NULL);
139
+ if (entry)
140
+ return entry->config;
141
+ return NULL;
142
+}
143
+
144
+static struct submodule *cache_lookup_name(struct submodule_cache *cache,
145
+ const unsigned char *gitmodules_sha1, const char *name)
146
+{
147
+ struct submodule_entry *entry;
148
+ unsigned int hash = hash_sha1_string(gitmodules_sha1, name);
149
+ struct submodule_entry key;
150
+ struct submodule key_config;
151
+
152
+ hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
153
+ key_config.name = name;
154
+
155
+ hashmap_entry_init(&key, hash);
156
+ key.config = &key_config;
157
+
158
+ entry = hashmap_get(&cache->for_name, &key, NULL);
159
+ if (entry)
160
+ return entry->config;
161
+ return NULL;
162
+}
163
+
164
+static int name_and_item_from_var(const char *var, struct strbuf *name,
165
+ struct strbuf *item)
166
+{
167
+ const char *subsection, *key;
168
+ int subsection_len, parse;
169
+ parse = parse_config_key(var, "submodule", &subsection,
170
+ &subsection_len, &key);
171
+ if (parse < 0 || !subsection)
172
+ return 0;
173
+
174
+ strbuf_add(name, subsection, subsection_len);
175
+ strbuf_addstr(item, key);
176
+
177
+ return 1;
178
+}
179
+
180
+static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
181
+ const unsigned char *gitmodules_sha1, const char *name)
182
+{
183
+ struct submodule *submodule;
184
+ struct strbuf name_buf = STRBUF_INIT;
185
+
186
+ submodule = cache_lookup_name(cache, gitmodules_sha1, name);
187
+ if (submodule)
188
+ return submodule;
189
+
190
+ submodule = xmalloc(sizeof(*submodule));
191
+
192
+ strbuf_addstr(&name_buf, name);
193
+ submodule->name = strbuf_detach(&name_buf, NULL);
194
+
195
+ submodule->path = NULL;
196
+ submodule->url = NULL;
197
+ submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
198
+ submodule->ignore = NULL;
199
+
200
+ hashcpy(submodule->gitmodules_sha1, gitmodules_sha1);
201
+
202
+ cache_add(cache, submodule);
203
+
204
+ return submodule;
205
+}
206
+
207
+static void warn_multiple_config(const unsigned char *commit_sha1,
208
+ const char *name, const char *option)
209
+{
210
+ const char *commit_string = "WORKTREE";
211
+ if (commit_sha1)
212
+ commit_string = sha1_to_hex(commit_sha1);
213
+ warning("%s:.gitmodules, multiple configurations found for "
214
+ "'submodule.%s.%s'. Skipping second one!",
215
+ commit_string, name, option);
216
+}
217
+
218
+struct parse_config_parameter {
219
+ struct submodule_cache *cache;
220
+ const unsigned char *commit_sha1;
221
+ const unsigned char *gitmodules_sha1;
222
+ int overwrite;
223
+};
224
+
225
+static int parse_config(const char *var, const char *value, void *data)
226
+{
227
+ struct parse_config_parameter *me = data;
228
+ struct submodule *submodule;
229
+ struct strbuf name = STRBUF_INIT, item = STRBUF_INIT;
230
+ int ret = 0;
231
+
232
+ /* this also ensures that we only parse submodule entries */
233
+ if (!name_and_item_from_var(var, &name, &item))
234
+ return 0;
235
+
236
+ submodule = lookup_or_create_by_name(me->cache, me->gitmodules_sha1,
237
+ name.buf);
238
+
239
+ if (!strcmp(item.buf, "path")) {
240
+ struct strbuf path = STRBUF_INIT;
241
+ if (!value) {
242
+ ret = config_error_nonbool(var);
243
+ goto release_return;
244
+ }
245
+ if (!me->overwrite && submodule->path != NULL) {
246
+ warn_multiple_config(me->commit_sha1, submodule->name,
247
+ "path");
248
+ goto release_return;
249
+ }
250
+
251
+ if (submodule->path)
252
+ cache_remove_path(me->cache, submodule);
253
+ free((void *) submodule->path);
254
+ strbuf_addstr(&path, value);
255
+ submodule->path = strbuf_detach(&path, NULL);
256
+ cache_put_path(me->cache, submodule);
257
+ } else if (!strcmp(item.buf, "fetchrecursesubmodules")) {
258
+ if (!me->overwrite &&
259
+ submodule->fetch_recurse != RECURSE_SUBMODULES_NONE) {
260
+ warn_multiple_config(me->commit_sha1, submodule->name,
261
+ "fetchrecursesubmodules");
262
+ goto release_return;
263
+ }
264
+
265
+ submodule->fetch_recurse = parse_fetch_recurse_submodules_arg(var, value);
266
+ } else if (!strcmp(item.buf, "ignore")) {
267
+ struct strbuf ignore = STRBUF_INIT;
268
+ if (!me->overwrite && submodule->ignore != NULL) {
269
+ warn_multiple_config(me->commit_sha1, submodule->name,
270
+ "ignore");
271
+ goto release_return;
272
+ }
273
+ if (!value) {
274
+ ret = config_error_nonbool(var);
275
+ goto release_return;
276
+ }
277
+ if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
278
+ strcmp(value, "all") && strcmp(value, "none")) {
279
+ warning("Invalid parameter '%s' for config option "
280
+ "'submodule.%s.ignore'", value, var);
281
+ goto release_return;
282
+ }
283
+
284
+ free((void *) submodule->ignore);
285
+ strbuf_addstr(&ignore, value);
286
+ submodule->ignore = strbuf_detach(&ignore, NULL);
287
+ } else if (!strcmp(item.buf, "url")) {
288
+ struct strbuf url = STRBUF_INIT;
289
+ if (!value) {
290
+ ret = config_error_nonbool(var);
291
+ goto release_return;
292
+ }
293
+ if (!me->overwrite && submodule->url != NULL) {
294
+ warn_multiple_config(me->commit_sha1, submodule->name,
295
+ "url");
296
+ goto release_return;
297
+ }
298
+
299
+ free((void *) submodule->url);
300
+ strbuf_addstr(&url, value);
301
+ submodule->url = strbuf_detach(&url, NULL);
302
+ }
303
+
304
+release_return:
305
+ strbuf_release(&name);
306
+ strbuf_release(&item);
307
+
308
+ return ret;
309
+}
310
+
311
+static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
312
+ unsigned char *gitmodules_sha1)
313
+{
314
+ struct strbuf rev = STRBUF_INIT;
315
+ int ret = 0;
316
+
317
+ if (is_null_sha1(commit_sha1)) {
318
+ hashcpy(gitmodules_sha1, null_sha1);
319
+ return 1;
320
+ }
321
+
322
+ strbuf_addf(&rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
323
+ if (get_sha1(rev.buf, gitmodules_sha1) >= 0)
324
+ ret = 1;
325
+
326
+ strbuf_release(&rev);
327
+ return ret;
328
+}
329
+
330
+/* This does a lookup of a submodule configuration by name or by path
331
+ * (key) with on-demand reading of the appropriate .gitmodules from
332
+ * revisions.
333
+ */
334
+static const struct submodule *config_from(struct submodule_cache *cache,
335
+ const unsigned char *commit_sha1, const char *key,
336
+ enum lookup_type lookup_type)
337
+{
338
+ struct strbuf rev = STRBUF_INIT;
339
+ unsigned long config_size;
340
+ char *config;
341
+ unsigned char sha1[20];
342
+ enum object_type type;
343
+ const struct submodule *submodule = NULL;
344
+ struct parse_config_parameter parameter;
345
+
346
+ /*
347
+ * If any parameter except the cache is a NULL pointer just
348
+ * return the first submodule. Can be used to check whether
349
+ * there are any submodules parsed.
350
+ */
351
+ if (!commit_sha1 || !key) {
352
+ struct hashmap_iter iter;
353
+ struct submodule_entry *entry;
354
+
355
+ hashmap_iter_init(&cache->for_name, &iter);
356
+ entry = hashmap_iter_next(&iter);
357
+ if (!entry)
358
+ return NULL;
359
+ return entry->config;
360
+ }
361
+
362
+ if (!gitmodule_sha1_from_commit(commit_sha1, sha1))
363
+ return NULL;
364
+
365
+ switch (lookup_type) {
366
+ case lookup_name:
367
+ submodule = cache_lookup_name(cache, sha1, key);
368
+ break;
369
+ case lookup_path:
370
+ submodule = cache_lookup_path(cache, sha1, key);
371
+ break;
372
+ }
373
+ if (submodule)
374
+ return submodule;
375
+
376
+ config = read_sha1_file(sha1, &type, &config_size);
377
+ if (!config)
378
+ return NULL;
379
+
380
+ if (type != OBJ_BLOB) {
381
+ free(config);
382
+ return NULL;
383
+ }
384
+
385
+ /* fill the submodule config into the cache */
386
+ parameter.cache = cache;
387
+ parameter.commit_sha1 = commit_sha1;
388
+ parameter.gitmodules_sha1 = sha1;
389
+ parameter.overwrite = 0;
390
+ git_config_from_buf(parse_config, rev.buf, config, config_size,
391
+ ¶meter);
392
+ free(config);
393
+
394
+ switch (lookup_type) {
395
+ case lookup_name:
396
+ return cache_lookup_name(cache, sha1, key);
397
+ case lookup_path:
398
+ return cache_lookup_path(cache, sha1, key);
399
+ default:
400
+ return NULL;
401
+ }
402
+}
403
+
404
+static const struct submodule *config_from_path(struct submodule_cache *cache,
405
+ const unsigned char *commit_sha1, const char *path)
406
+{
407
+ return config_from(cache, commit_sha1, path, lookup_path);
408
+}
409
+
410
+static const struct submodule *config_from_name(struct submodule_cache *cache,
411
+ const unsigned char *commit_sha1, const char *name)
412
+{
413
+ return config_from(cache, commit_sha1, name, lookup_name);
414
+}
415
+
416
+static void ensure_cache_init(void)
417
+{
418
+ if (is_cache_init)
419
+ return;
420
+
421
+ cache_init(&cache);
422
+ is_cache_init = 1;
423
+}
424
+
425
+const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
426
+ const char *name)
427
+{
428
+ ensure_cache_init();
429
+ return config_from_name(&cache, commit_sha1, name);
430
+}
431
+
432
+const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
433
+ const char *path)
434
+{
435
+ ensure_cache_init();
436
+ return config_from_path(&cache, commit_sha1, path);
437
+}
438
+
439
+void submodule_free(void)
440
+{
441
+ cache_free(&cache);
442
+ is_cache_init = 0;
443
+}