submodule-config.con commit submodule-config.c: drop hashmap_cmp_fn cast (152cbdc)
   1#include "cache.h"
   2#include "config.h"
   3#include "submodule-config.h"
   4#include "submodule.h"
   5#include "strbuf.h"
   6
   7/*
   8 * submodule cache lookup structure
   9 * There is one shared set of 'struct submodule' entries which can be
  10 * looked up by their sha1 blob id of the .gitmodule file and either
  11 * using path or name as key.
  12 * for_path stores submodule entries with path as key
  13 * for_name stores submodule entries with name as key
  14 */
  15struct submodule_cache {
  16        struct hashmap for_path;
  17        struct hashmap for_name;
  18};
  19
  20/*
  21 * thin wrapper struct needed to insert 'struct submodule' entries to
  22 * the hashmap
  23 */
  24struct submodule_entry {
  25        struct hashmap_entry ent;
  26        struct submodule *config;
  27};
  28
  29enum lookup_type {
  30        lookup_name,
  31        lookup_path
  32};
  33
  34static struct submodule_cache the_submodule_cache;
  35static int is_cache_init;
  36
  37static int config_path_cmp(const void *unused_cmp_data,
  38                           const void *entry,
  39                           const void *entry_or_key,
  40                           const void *unused_keydata)
  41{
  42        const struct submodule_entry *a = entry;
  43        const struct submodule_entry *b = entry_or_key;
  44
  45        return strcmp(a->config->path, b->config->path) ||
  46               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  47}
  48
  49static int config_name_cmp(const void *unused_cmp_data,
  50                           const void *entry,
  51                           const void *entry_or_key,
  52                           const void *unused_keydata)
  53{
  54        const struct submodule_entry *a = entry;
  55        const struct submodule_entry *b = entry_or_key;
  56
  57        return strcmp(a->config->name, b->config->name) ||
  58               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  59}
  60
  61static void cache_init(struct submodule_cache *cache)
  62{
  63        hashmap_init(&cache->for_path, config_path_cmp, NULL, 0);
  64        hashmap_init(&cache->for_name, config_name_cmp, NULL, 0);
  65}
  66
  67static void free_one_config(struct submodule_entry *entry)
  68{
  69        free((void *) entry->config->path);
  70        free((void *) entry->config->name);
  71        free((void *) entry->config->branch);
  72        free((void *) entry->config->update_strategy.command);
  73        free(entry->config);
  74}
  75
  76static void cache_free(struct submodule_cache *cache)
  77{
  78        struct hashmap_iter iter;
  79        struct submodule_entry *entry;
  80
  81        /*
  82         * We iterate over the name hash here to be symmetric with the
  83         * allocation of struct submodule entries. Each is allocated by
  84         * their .gitmodule blob sha1 and submodule name.
  85         */
  86        hashmap_iter_init(&cache->for_name, &iter);
  87        while ((entry = hashmap_iter_next(&iter)))
  88                free_one_config(entry);
  89
  90        hashmap_free(&cache->for_path, 1);
  91        hashmap_free(&cache->for_name, 1);
  92}
  93
  94static unsigned int hash_sha1_string(const unsigned char *sha1,
  95                                     const char *string)
  96{
  97        return memhash(sha1, 20) + strhash(string);
  98}
  99
 100static void cache_put_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 = xmalloc(sizeof(*e));
 106        hashmap_entry_init(e, hash);
 107        e->config = submodule;
 108        hashmap_put(&cache->for_path, e);
 109}
 110
 111static void cache_remove_path(struct submodule_cache *cache,
 112                              struct submodule *submodule)
 113{
 114        unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
 115                                             submodule->path);
 116        struct submodule_entry e;
 117        struct submodule_entry *removed;
 118        hashmap_entry_init(&e, hash);
 119        e.config = submodule;
 120        removed = hashmap_remove(&cache->for_path, &e, NULL);
 121        free(removed);
 122}
 123
 124static void cache_add(struct submodule_cache *cache,
 125                      struct submodule *submodule)
 126{
 127        unsigned int hash = hash_sha1_string(submodule->gitmodules_sha1,
 128                                             submodule->name);
 129        struct submodule_entry *e = xmalloc(sizeof(*e));
 130        hashmap_entry_init(e, hash);
 131        e->config = submodule;
 132        hashmap_add(&cache->for_name, e);
 133}
 134
 135static const struct submodule *cache_lookup_path(struct submodule_cache *cache,
 136                const unsigned char *gitmodules_sha1, const char *path)
 137{
 138        struct submodule_entry *entry;
 139        unsigned int hash = hash_sha1_string(gitmodules_sha1, path);
 140        struct submodule_entry key;
 141        struct submodule key_config;
 142
 143        hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
 144        key_config.path = path;
 145
 146        hashmap_entry_init(&key, hash);
 147        key.config = &key_config;
 148
 149        entry = hashmap_get(&cache->for_path, &key, NULL);
 150        if (entry)
 151                return entry->config;
 152        return NULL;
 153}
 154
 155static struct submodule *cache_lookup_name(struct submodule_cache *cache,
 156                const unsigned char *gitmodules_sha1, const char *name)
 157{
 158        struct submodule_entry *entry;
 159        unsigned int hash = hash_sha1_string(gitmodules_sha1, name);
 160        struct submodule_entry key;
 161        struct submodule key_config;
 162
 163        hashcpy(key_config.gitmodules_sha1, gitmodules_sha1);
 164        key_config.name = name;
 165
 166        hashmap_entry_init(&key, hash);
 167        key.config = &key_config;
 168
 169        entry = hashmap_get(&cache->for_name, &key, NULL);
 170        if (entry)
 171                return entry->config;
 172        return NULL;
 173}
 174
 175static int name_and_item_from_var(const char *var, struct strbuf *name,
 176                                  struct strbuf *item)
 177{
 178        const char *subsection, *key;
 179        int subsection_len, parse;
 180        parse = parse_config_key(var, "submodule", &subsection,
 181                        &subsection_len, &key);
 182        if (parse < 0 || !subsection)
 183                return 0;
 184
 185        strbuf_add(name, subsection, subsection_len);
 186        strbuf_addstr(item, key);
 187
 188        return 1;
 189}
 190
 191static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache,
 192                const unsigned char *gitmodules_sha1, const char *name)
 193{
 194        struct submodule *submodule;
 195        struct strbuf name_buf = STRBUF_INIT;
 196
 197        submodule = cache_lookup_name(cache, gitmodules_sha1, name);
 198        if (submodule)
 199                return submodule;
 200
 201        submodule = xmalloc(sizeof(*submodule));
 202
 203        strbuf_addstr(&name_buf, name);
 204        submodule->name = strbuf_detach(&name_buf, NULL);
 205
 206        submodule->path = NULL;
 207        submodule->url = NULL;
 208        submodule->update_strategy.type = SM_UPDATE_UNSPECIFIED;
 209        submodule->update_strategy.command = NULL;
 210        submodule->fetch_recurse = RECURSE_SUBMODULES_NONE;
 211        submodule->ignore = NULL;
 212        submodule->branch = NULL;
 213        submodule->recommend_shallow = -1;
 214
 215        hashcpy(submodule->gitmodules_sha1, gitmodules_sha1);
 216
 217        cache_add(cache, submodule);
 218
 219        return submodule;
 220}
 221
 222static int parse_fetch_recurse(const char *opt, const char *arg,
 223                               int die_on_error)
 224{
 225        switch (git_config_maybe_bool(opt, arg)) {
 226        case 1:
 227                return RECURSE_SUBMODULES_ON;
 228        case 0:
 229                return RECURSE_SUBMODULES_OFF;
 230        default:
 231                if (!strcmp(arg, "on-demand"))
 232                        return RECURSE_SUBMODULES_ON_DEMAND;
 233
 234                if (die_on_error)
 235                        die("bad %s argument: %s", opt, arg);
 236                else
 237                        return RECURSE_SUBMODULES_ERROR;
 238        }
 239}
 240
 241int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
 242{
 243        return parse_fetch_recurse(opt, arg, 1);
 244}
 245
 246static int parse_update_recurse(const char *opt, const char *arg,
 247                                int die_on_error)
 248{
 249        switch (git_config_maybe_bool(opt, arg)) {
 250        case 1:
 251                return RECURSE_SUBMODULES_ON;
 252        case 0:
 253                return RECURSE_SUBMODULES_OFF;
 254        default:
 255                if (die_on_error)
 256                        die("bad %s argument: %s", opt, arg);
 257                return RECURSE_SUBMODULES_ERROR;
 258        }
 259}
 260
 261int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
 262{
 263        return parse_update_recurse(opt, arg, 1);
 264}
 265
 266static int parse_push_recurse(const char *opt, const char *arg,
 267                               int die_on_error)
 268{
 269        switch (git_config_maybe_bool(opt, arg)) {
 270        case 1:
 271                /* There's no simple "on" value when pushing */
 272                if (die_on_error)
 273                        die("bad %s argument: %s", opt, arg);
 274                else
 275                        return RECURSE_SUBMODULES_ERROR;
 276        case 0:
 277                return RECURSE_SUBMODULES_OFF;
 278        default:
 279                if (!strcmp(arg, "on-demand"))
 280                        return RECURSE_SUBMODULES_ON_DEMAND;
 281                else if (!strcmp(arg, "check"))
 282                        return RECURSE_SUBMODULES_CHECK;
 283                else if (!strcmp(arg, "only"))
 284                        return RECURSE_SUBMODULES_ONLY;
 285                else if (die_on_error)
 286                        die("bad %s argument: %s", opt, arg);
 287                else
 288                        return RECURSE_SUBMODULES_ERROR;
 289        }
 290}
 291
 292int parse_push_recurse_submodules_arg(const char *opt, const char *arg)
 293{
 294        return parse_push_recurse(opt, arg, 1);
 295}
 296
 297static void warn_multiple_config(const unsigned char *treeish_name,
 298                                 const char *name, const char *option)
 299{
 300        const char *commit_string = "WORKTREE";
 301        if (treeish_name)
 302                commit_string = sha1_to_hex(treeish_name);
 303        warning("%s:.gitmodules, multiple configurations found for "
 304                        "'submodule.%s.%s'. Skipping second one!",
 305                        commit_string, name, option);
 306}
 307
 308struct parse_config_parameter {
 309        struct submodule_cache *cache;
 310        const unsigned char *treeish_name;
 311        const unsigned char *gitmodules_sha1;
 312        int overwrite;
 313};
 314
 315static int parse_config(const char *var, const char *value, void *data)
 316{
 317        struct parse_config_parameter *me = data;
 318        struct submodule *submodule;
 319        struct strbuf name = STRBUF_INIT, item = STRBUF_INIT;
 320        int ret = 0;
 321
 322        /* this also ensures that we only parse submodule entries */
 323        if (!name_and_item_from_var(var, &name, &item))
 324                return 0;
 325
 326        submodule = lookup_or_create_by_name(me->cache,
 327                                             me->gitmodules_sha1,
 328                                             name.buf);
 329
 330        if (!strcmp(item.buf, "path")) {
 331                if (!value)
 332                        ret = config_error_nonbool(var);
 333                else if (!me->overwrite && submodule->path)
 334                        warn_multiple_config(me->treeish_name, submodule->name,
 335                                        "path");
 336                else {
 337                        if (submodule->path)
 338                                cache_remove_path(me->cache, submodule);
 339                        free((void *) submodule->path);
 340                        submodule->path = xstrdup(value);
 341                        cache_put_path(me->cache, submodule);
 342                }
 343        } else if (!strcmp(item.buf, "fetchrecursesubmodules")) {
 344                /* when parsing worktree configurations we can die early */
 345                int die_on_error = is_null_sha1(me->gitmodules_sha1);
 346                if (!me->overwrite &&
 347                    submodule->fetch_recurse != RECURSE_SUBMODULES_NONE)
 348                        warn_multiple_config(me->treeish_name, submodule->name,
 349                                        "fetchrecursesubmodules");
 350                else
 351                        submodule->fetch_recurse = parse_fetch_recurse(
 352                                                                var, value,
 353                                                                die_on_error);
 354        } else if (!strcmp(item.buf, "ignore")) {
 355                if (!value)
 356                        ret = config_error_nonbool(var);
 357                else if (!me->overwrite && submodule->ignore)
 358                        warn_multiple_config(me->treeish_name, submodule->name,
 359                                        "ignore");
 360                else if (strcmp(value, "untracked") &&
 361                         strcmp(value, "dirty") &&
 362                         strcmp(value, "all") &&
 363                         strcmp(value, "none"))
 364                        warning("Invalid parameter '%s' for config option "
 365                                        "'submodule.%s.ignore'", value, name.buf);
 366                else {
 367                        free((void *) submodule->ignore);
 368                        submodule->ignore = xstrdup(value);
 369                }
 370        } else if (!strcmp(item.buf, "url")) {
 371                if (!value) {
 372                        ret = config_error_nonbool(var);
 373                } else if (!me->overwrite && submodule->url) {
 374                        warn_multiple_config(me->treeish_name, submodule->name,
 375                                        "url");
 376                } else {
 377                        free((void *) submodule->url);
 378                        submodule->url = xstrdup(value);
 379                }
 380        } else if (!strcmp(item.buf, "update")) {
 381                if (!value)
 382                        ret = config_error_nonbool(var);
 383                else if (!me->overwrite &&
 384                         submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)
 385                        warn_multiple_config(me->treeish_name, submodule->name,
 386                                             "update");
 387                else if (parse_submodule_update_strategy(value,
 388                         &submodule->update_strategy) < 0)
 389                                die(_("invalid value for %s"), var);
 390        } else if (!strcmp(item.buf, "shallow")) {
 391                if (!me->overwrite && submodule->recommend_shallow != -1)
 392                        warn_multiple_config(me->treeish_name, submodule->name,
 393                                             "shallow");
 394                else
 395                        submodule->recommend_shallow =
 396                                git_config_bool(var, value);
 397        } else if (!strcmp(item.buf, "branch")) {
 398                if (!me->overwrite && submodule->branch)
 399                        warn_multiple_config(me->treeish_name, submodule->name,
 400                                             "branch");
 401                else {
 402                        free((void *)submodule->branch);
 403                        submodule->branch = xstrdup(value);
 404                }
 405        }
 406
 407        strbuf_release(&name);
 408        strbuf_release(&item);
 409
 410        return ret;
 411}
 412
 413int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
 414                                      unsigned char *gitmodules_sha1,
 415                                      struct strbuf *rev)
 416{
 417        int ret = 0;
 418
 419        if (is_null_sha1(treeish_name)) {
 420                hashclr(gitmodules_sha1);
 421                return 1;
 422        }
 423
 424        strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
 425        if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
 426                ret = 1;
 427
 428        return ret;
 429}
 430
 431/* This does a lookup of a submodule configuration by name or by path
 432 * (key) with on-demand reading of the appropriate .gitmodules from
 433 * revisions.
 434 */
 435static const struct submodule *config_from(struct submodule_cache *cache,
 436                const unsigned char *treeish_name, const char *key,
 437                enum lookup_type lookup_type)
 438{
 439        struct strbuf rev = STRBUF_INIT;
 440        unsigned long config_size;
 441        char *config = NULL;
 442        unsigned char sha1[20];
 443        enum object_type type;
 444        const struct submodule *submodule = NULL;
 445        struct parse_config_parameter parameter;
 446
 447        /*
 448         * If any parameter except the cache is a NULL pointer just
 449         * return the first submodule. Can be used to check whether
 450         * there are any submodules parsed.
 451         */
 452        if (!treeish_name || !key) {
 453                struct hashmap_iter iter;
 454                struct submodule_entry *entry;
 455
 456                entry = hashmap_iter_first(&cache->for_name, &iter);
 457                if (!entry)
 458                        return NULL;
 459                return entry->config;
 460        }
 461
 462        if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
 463                goto out;
 464
 465        switch (lookup_type) {
 466        case lookup_name:
 467                submodule = cache_lookup_name(cache, sha1, key);
 468                break;
 469        case lookup_path:
 470                submodule = cache_lookup_path(cache, sha1, key);
 471                break;
 472        }
 473        if (submodule)
 474                goto out;
 475
 476        config = read_sha1_file(sha1, &type, &config_size);
 477        if (!config || type != OBJ_BLOB)
 478                goto out;
 479
 480        /* fill the submodule config into the cache */
 481        parameter.cache = cache;
 482        parameter.treeish_name = treeish_name;
 483        parameter.gitmodules_sha1 = sha1;
 484        parameter.overwrite = 0;
 485        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
 486                        config, config_size, &parameter);
 487        strbuf_release(&rev);
 488        free(config);
 489
 490        switch (lookup_type) {
 491        case lookup_name:
 492                return cache_lookup_name(cache, sha1, key);
 493        case lookup_path:
 494                return cache_lookup_path(cache, sha1, key);
 495        default:
 496                return NULL;
 497        }
 498
 499out:
 500        strbuf_release(&rev);
 501        free(config);
 502        return submodule;
 503}
 504
 505static void ensure_cache_init(void)
 506{
 507        if (is_cache_init)
 508                return;
 509
 510        cache_init(&the_submodule_cache);
 511        is_cache_init = 1;
 512}
 513
 514int parse_submodule_config_option(const char *var, const char *value)
 515{
 516        struct parse_config_parameter parameter;
 517        parameter.cache = &the_submodule_cache;
 518        parameter.treeish_name = NULL;
 519        parameter.gitmodules_sha1 = null_sha1;
 520        parameter.overwrite = 1;
 521
 522        ensure_cache_init();
 523        return parse_config(var, value, &parameter);
 524}
 525
 526const struct submodule *submodule_from_name(const unsigned char *treeish_name,
 527                const char *name)
 528{
 529        ensure_cache_init();
 530        return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
 531}
 532
 533const struct submodule *submodule_from_path(const unsigned char *treeish_name,
 534                const char *path)
 535{
 536        ensure_cache_init();
 537        return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
 538}
 539
 540void submodule_free(void)
 541{
 542        cache_free(&the_submodule_cache);
 543        is_cache_init = 0;
 544}