/* * Try locking path, retrying with quadratic backoff for at least * timeout_ms milliseconds. If timeout_ms is 0, try locking the file * exactly once. If timeout_ms is -1, try indefinitely. */ static int lock_file_timeout(struct lock_file *lk, const char *path, int flags, long timeout_ms) { int n = 1; int multiplier = 1; long remaining_ms = 0; static int random_initialized = 0; if (timeout_ms == 0) return lock_file(lk, path, flags); if (!random_initialized) { srand((unsigned int)getpid()); random_initialized = 1; } if (timeout_ms > 0) remaining_ms = timeout_ms; while (1) { long backoff_ms, wait_ms; int fd; fd = lock_file(lk, path, flags); if (fd >= 0) return fd; /* success */ else if (errno != EEXIST) return -1; /* failure other than lock held */ else if (timeout_ms > 0 && remaining_ms <= 0) return -1; /* failure due to timeout */ backoff_ms = multiplier * INITIAL_BACKOFF_MS; /* back off for between 0.75*backoff_ms and 1.25*backoff_ms */ wait_ms = (750 + rand() % 500) * backoff_ms / 1000; sleep_millisec(wait_ms); remaining_ms -= wait_ms; /* Recursion: (n+1)^2 = n^2 + 2n + 1 */ multiplier += 2*n + 1; if (multiplier > BACKOFF_MAX_MULTIPLIER) multiplier = BACKOFF_MAX_MULTIPLIER; else n++; } }
const char *help_unknown_cmd(const char *cmd) { int i, n, best_similarity = 0; struct cmdnames main_cmds, other_cmds; struct cmdname_help *common_cmds; memset(&main_cmds, 0, sizeof(main_cmds)); memset(&other_cmds, 0, sizeof(other_cmds)); memset(&aliases, 0, sizeof(aliases)); read_early_config(git_unknown_cmd_config, NULL); load_command_list("git-", &main_cmds, &other_cmds); add_cmd_list(&main_cmds, &aliases); add_cmd_list(&main_cmds, &other_cmds); QSORT(main_cmds.names, main_cmds.cnt, cmdname_compare); uniq(&main_cmds); extract_cmds(&common_cmds, common_mask); /* This abuses cmdname->len for levenshtein distance */ for (i = 0, n = 0; i < main_cmds.cnt; i++) { int cmp = 0; /* avoid compiler stupidity */ const char *candidate = main_cmds.names[i]->name; /* * An exact match means we have the command, but * for some reason exec'ing it gave us ENOENT; probably * it's a bad interpreter in the #! line. */ if (!strcmp(candidate, cmd)) die(_(bad_interpreter_advice), cmd, cmd); /* Does the candidate appear in common_cmds list? */ while (common_cmds[n].name && (cmp = strcmp(common_cmds[n].name, candidate)) < 0) n++; if (common_cmds[n].name && !cmp) { /* Yes, this is one of the common commands */ n++; /* use the entry from common_cmds[] */ if (starts_with(candidate, cmd)) { /* Give prefix match a very good score */ main_cmds.names[i]->len = 0; continue; } } main_cmds.names[i]->len = levenshtein(cmd, candidate, 0, 2, 1, 3) + 1; } FREE_AND_NULL(common_cmds); QSORT(main_cmds.names, main_cmds.cnt, levenshtein_compare); if (!main_cmds.cnt) die(_("Uh oh. Your system reports no Git commands at all.")); /* skip and count prefix matches */ for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++) ; /* still counting */ if (main_cmds.cnt <= n) { /* prefix matches with everything? that is too ambiguous */ best_similarity = SIMILARITY_FLOOR + 1; } else { /* count all the most similar ones */ for (best_similarity = main_cmds.names[n++]->len; (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len); n++) ; /* still counting */ } if (autocorrect && n == 1 && SIMILAR_ENOUGH(best_similarity)) { const char *assumed = main_cmds.names[0]->name; main_cmds.names[0] = NULL; clean_cmdnames(&main_cmds); fprintf_ln(stderr, _("WARNING: You called a Git command named '%s', " "which does not exist."), cmd); if (autocorrect < 0) fprintf_ln(stderr, _("Continuing under the assumption that " "you meant '%s'."), assumed); else { fprintf_ln(stderr, _("Continuing in %0.1f seconds, " "assuming that you meant '%s'."), (float)autocorrect/10.0, assumed); sleep_millisec(autocorrect * 100); } return assumed; } fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd); if (SIMILAR_ENOUGH(best_similarity)) { fprintf_ln(stderr, Q_("\nThe most similar command is", "\nThe most similar commands are", n)); for (i = 0; i < n; i++) fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); } exit(1); }