static struct complete_reflogs *read_complete_reflog(const char *ref) { struct complete_reflogs *reflogs = xcalloc(1, sizeof(struct complete_reflogs)); reflogs->ref = xstrdup(ref); for_each_reflog_ent(ref, read_one_reflog, reflogs); if (reflogs->nr == 0) { struct object_id oid; const char *name; void *name_to_free; name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING, oid.hash, NULL); if (name) { for_each_reflog_ent(name, read_one_reflog, reflogs); free(name_to_free); } } if (reflogs->nr == 0) { char *refname = xstrfmt("refs/%s", ref); for_each_reflog_ent(refname, read_one_reflog, reflogs); if (reflogs->nr == 0) { free(refname); refname = xstrfmt("refs/heads/%s", ref); for_each_reflog_ent(refname, read_one_reflog, reflogs); } free(refname); } return reflogs; }
static void name_rev(struct commit *commit, const char *tip_name, int generation, int distance, int deref) { struct rev_name *name = (struct rev_name *)commit->util; struct commit_list *parents; int parent_number = 1; parse_commit(commit); if (commit->date < cutoff) return; if (deref) { tip_name = xstrfmt("%s^0", tip_name); if (generation) die("generation: %d, but deref?", generation); } if (name == NULL) { name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; } else if (name->distance > distance) { copy_data: name->tip_name = tip_name; name->generation = generation; name->distance = distance; } else return; for (parents = commit->parents; parents; parents = parents->next, parent_number++) { if (parent_number > 1) { size_t len; char *new_name; strip_suffix(tip_name, "^0", &len); if (generation > 0) new_name = xstrfmt("%.*s~%d^%d", (int)len, tip_name, generation, parent_number); else new_name = xstrfmt("%.*s^%d", (int)len, tip_name, parent_number); name_rev(parents->item, new_name, 0, distance + MERGE_TRAVERSAL_WEIGHT, 0); } else { name_rev(parents->item, tip_name, generation + 1, distance + 1, 0); } } }
static int bisect_next_check(const struct bisect_terms *terms, const char *current_term) { int missing_good = 1, missing_bad = 1, retval = 0; const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad); const char *good_glob = xstrfmt("%s-*", terms->term_good); if (ref_exists(bad_ref)) missing_bad = 0; for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/", (void *) &missing_good); if (!missing_good && !missing_bad) goto finish; if (!current_term) { retval = -1; goto finish; } if (missing_good && !missing_bad && !strcmp(current_term, terms->term_good)) { char *yesno; /* * have bad (or new) but not good (or old). We could bisect * although this is less optimum. */ warning(_("bisecting only with a %s commit"), terms->term_bad); if (!isatty(0)) goto finish; /* * TRANSLATORS: Make sure to include [Y] and [n] in your * translation. The program will only accept English input * at this point. */ yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO); if (starts_with(yesno, "N") || starts_with(yesno, "n")) retval = -1; goto finish; } if (!is_empty_or_missing_file(git_path_bisect_start())) { retval = error(_(need_bad_and_good_revision_warning), vocab_bad, vocab_good, vocab_bad, vocab_good); } else { retval = error(_(need_bisect_start_warning), vocab_good, vocab_bad, vocab_good, vocab_bad); } finish: free((void *) good_glob); free((void *) bad_ref); return retval; }
static int check_term_format(const char *term, const char *orig_term) { int res; char *new_term = xstrfmt("refs/bisect/%s", term); res = check_refname_format(new_term, 0); free(new_term); if (res) return error(_("'%s' is not a valid term"), term); if (one_of(term, "help", "start", "skip", "next", "reset", "visualize", "view", "replay", "log", "run", "terms", NULL)) return error(_("can't use the builtin command '%s' as a term"), term); /* * In theory, nothing prevents swapping completely good and bad, * but this situation could be confusing and hasn't been tested * enough. Forbid it for now. */ if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) || (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL))) return error(_("can't change the meaning of the term '%s'"), term); return 0; }
/* * Normalize "path", prepending the "prefix" for relative paths. If * remaining_prefix is not NULL, return the actual prefix still * remains in the path. For example, prefix = sub1/sub2/ and path is * * foo -> sub1/sub2/foo (full prefix) * ../foo -> sub1/foo (remaining prefix is sub1/) * ../../bar -> bar (no remaining prefix) * ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix) * `pwd`/../bar -> sub1/bar (no remaining prefix) */ char *prefix_path_gently(const char *prefix, int len, int *remaining_prefix, const char *path) { const char *orig = path; char *sanitized; if (is_absolute_path(orig)) { sanitized = xmalloc(strlen(path) + 1); if (remaining_prefix) *remaining_prefix = 0; if (normalize_path_copy_len(sanitized, path, remaining_prefix)) { free(sanitized); return NULL; } if (abspath_part_inside_repo(sanitized)) { free(sanitized); return NULL; } } else { sanitized = xstrfmt("%.*s%s", len, prefix, path); if (remaining_prefix) *remaining_prefix = len; if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix)) { free(sanitized); return NULL; } } return sanitized; }
static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format) { struct ref_array array; char *to_free = NULL; int i; memset(&array, 0, sizeof(array)); if (filter->lines == -1) filter->lines = 0; if (!format) { if (filter->lines) { to_free = xstrfmt("%s %%(contents:lines=%d)", "%(align:15)%(refname:strip=2)%(end)", filter->lines); format = to_free; } else format = "%(refname:strip=2)"; } verify_ref_format(format); filter->with_commit_tag_algo = 1; filter_refs(&array, filter, FILTER_REFS_TAGS); ref_array_sort(sorting, &array); for (i = 0; i < array.nr; i++) show_ref_array_item(array.items[i], format, 0); ref_array_clear(&array); free(to_free); return 0; }
static int get_message(struct commit *commit, struct commit_message *out) { const char *abbrev, *subject; int subject_len; out->message = logmsg_reencode(commit, NULL, get_commit_output_encoding()); abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV); subject_len = find_commit_subject(out->message, &subject); out->subject = xmemdupz(subject, subject_len); out->label = xstrfmt("%s... %s", abbrev, out->subject); out->parent_label = xstrfmt("parent of %s", out->label); return 0; }
static void fetch_symref(const char *path, char **symref, struct object_id *oid) { char *url = xstrfmt("%s%s", repo->url, path); struct strbuf buffer = STRBUF_INIT; const char *name; if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK) die("Couldn't get %s for remote symref\n%s", url, curl_errorstr); free(url); FREE_AND_NULL(*symref); oidclr(oid); if (buffer.len == 0) return; /* Cut off trailing newline. */ strbuf_rtrim(&buffer); /* If it's a symref, set the refname; otherwise try for a sha1 */ if (skip_prefix(buffer.buf, "ref: ", &name)) { *symref = xmemdupz(name, buffer.len - (name - buffer.buf)); } else { get_oid_hex(buffer.buf, oid); } strbuf_release(&buffer); }
static void name_rev(struct commit *commit, const char *tip_name, int generation, int distance, int deref) { struct rev_name *name = (struct rev_name *)commit->util; struct commit_list *parents; int parent_number = 1; parse_commit(commit); if (commit->date < cutoff) return; if (deref) { tip_name = xstrfmt("%s^0", tip_name); if (generation) die("generation: %d, but deref?", generation); } if (name == NULL) { name = xmalloc(sizeof(rev_name)); commit->util = name; goto copy_data; } else if (name->distance > distance) { copy_data: name->tip_name = tip_name; name->generation = generation; name->distance = distance; } else return; for (parents = commit->parents; parents; parents = parents->next, parent_number++) { if (parent_number > 1) { int len = strlen(tip_name); char *new_name = xmalloc(len + 1 + decimal_length(generation) + /* ~<n> */ 1 + 2 + /* ^NN */ 1); if (len > 2 && !strcmp(tip_name + len - 2, "^0")) len -= 2; if (generation > 0) sprintf(new_name, "%.*s~%d^%d", len, tip_name, generation, parent_number); else sprintf(new_name, "%.*s^%d", len, tip_name, parent_number); name_rev(parents->item, new_name, 0, distance + MERGE_TRAVERSAL_WEIGHT, 0); } else { name_rev(parents->item, tip_name, generation + 1, distance + 1, 0); } } }
void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, const char *cmd) { int i; const char **msgs = opts->msgs; const char *msg; const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches"; if (advice_commit_before_merge) msg = "Your local changes to the following files would be overwritten by %s:\n%%s" "Please, commit your changes or stash them before you can %s."; else msg = "Your local changes to the following files would be overwritten by %s:\n%%s"; msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] = xstrfmt(msg, cmd, cmd2); msgs[ERROR_NOT_UPTODATE_DIR] = "Updating the following directories would lose untracked files in it:\n%s"; if (advice_commit_before_merge) msg = "The following untracked working tree files would be %s by %s:\n%%s" "Please move or remove them before you can %s."; else msg = "The following untracked working tree files would be %s by %s:\n%%s"; msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2); msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2); /* * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we * cannot easily display it as a list. */ msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'. Cannot bind."; msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] = "Cannot update sparse checkout: the following entries are not up-to-date:\n%s"; msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] = "The following Working tree files would be overwritten by sparse checkout update:\n%s"; msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] = "The following Working tree files would be removed by sparse checkout update:\n%s"; opts->show_all_errors = 1; /* rejected paths may not have a static buffer */ for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++) opts->unpack_rejects[i].strdup_strings = 1; }
static void expand_base_dir(char **out, const char *in, const char *base_dir, const char *def_in) { free(*out); if (in) *out = xstrdup(in); else *out = xstrfmt("%s/%s", base_dir, def_in); }
static char *git_path_from_env(const char *envvar, const char *git_dir, const char *path, int fromenv) { if (fromenv) { const char *value = getenv(envvar); if (value) return xstrdup(value); } return xstrfmt("%s/%s", git_dir, path); }
static void show_datestring(const char *flag, const char *datestr) { char *buffer; /* date handling requires both flags and revs */ if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS)) return; buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr)); show(buffer); free(buffer); }
const char *unique_tracking_name(const char *name, struct object_id *oid) { struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 }; cb_data.src_ref = xstrfmt("refs/heads/%s", name); cb_data.dst_oid = oid; for_each_remote(check_tracking_name, &cb_data); free(cb_data.src_ref); if (cb_data.unique) return cb_data.dst_ref; free(cb_data.dst_ref); return NULL; }
static char *get_socket_path(void) { struct stat sb; char *old_dir, *socket; old_dir = expand_user_path("~/.git-credential-cache", 0); if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode)) socket = xstrfmt("%s/socket", old_dir); else socket = xdg_cache_home("credential/socket"); free(old_dir); return socket; }
/* * Match one itself and its subtrees with two and pick the best match. */ static void match_trees(const unsigned char *hash1, const unsigned char *hash2, int *best_score, char **best_match, const char *base, int recurse_limit) { struct tree_desc one; void *one_buf = fill_tree_desc_strict(&one, hash1); while (one.size) { const char *path; const unsigned char *elem; unsigned mode; int score; elem = tree_entry_extract(&one, &path, &mode); if (!S_ISDIR(mode)) goto next; score = score_trees(elem, hash2); if (*best_score < score) { free(*best_match); *best_match = xstrfmt("%s%s", base, path); *best_score = score; } if (recurse_limit) { char *newbase = xstrfmt("%s%s/", base, path); match_trees(elem, hash2, best_score, best_match, newbase, recurse_limit - 1); free(newbase); } next: update_tree_entry(&one); } free(one_buf); }
static void log_commit(FILE *fp, char *fmt, const char *state, struct commit *commit) { struct pretty_print_context pp = {0}; struct strbuf commit_msg = STRBUF_INIT; char *label = xstrfmt(fmt, state); format_commit_message(commit, "%s", &commit_msg, &pp); fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid), commit_msg.buf); strbuf_release(&commit_msg); free(label); }
/* * Initialize 'submodule' as the submodule given by 'path' in parent repository * 'superproject'. * Return 0 upon success and a non-zero value upon failure. */ int repo_submodule_init(struct repository *submodule, struct repository *superproject, const char *path) { const struct submodule *sub; struct strbuf gitdir = STRBUF_INIT; struct strbuf worktree = STRBUF_INIT; int ret = 0; sub = submodule_from_path(superproject, &null_oid, path); if (!sub) { ret = -1; goto out; } strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path); strbuf_repo_worktree_path(&worktree, superproject, "%s", path); if (repo_init(submodule, gitdir.buf, worktree.buf)) { /* * If initilization fails then it may be due to the submodule * not being populated in the superproject's worktree. Instead * we can try to initilize the submodule by finding it's gitdir * in the superproject's 'modules' directory. In this case the * submodule would not have a worktree. */ strbuf_reset(&gitdir); strbuf_repo_git_path(&gitdir, superproject, "modules/%s", sub->name); if (repo_init(submodule, gitdir.buf, NULL)) { ret = -1; goto out; } } submodule->submodule_prefix = xstrfmt("%s%s/", superproject->submodule_prefix ? superproject->submodule_prefix : "", path); out: strbuf_release(&gitdir); strbuf_release(&worktree); return ret; }
/* * Is there one among the list of patterns that match the tail part * of the path? */ static int tail_match(const char **pattern, const char *path) { const char *p; char *pathbuf; if (!pattern) return 1; /* no restriction */ pathbuf = xstrfmt("/%s", path); while ((p = *(pattern++)) != NULL) { if (!wildmatch(p, pathbuf, 0, NULL)) { free(pathbuf); return 1; } } free(pathbuf); return 0; }
static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, timestamp_t timestamp) { struct object *obj; if (!is_null_oid(oid)) { obj = lookup_object(oid->hash); if (obj && (obj->flags & HAS_OBJ)) { if (timestamp && name_objects) add_decoration(fsck_walk_options.object_names, obj, xstrfmt("%s@{%"PRItime"}", refname, timestamp)); obj->flags |= USED; mark_object_reachable(obj); } else if (!is_promisor_object(oid)) { error("%s: invalid reflog entry %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; } } }
/* * Strip username (and password) from a URL and return * it in a newly allocated string. */ char *transport_anonymize_url(const char *url) { char *scheme_prefix, *anon_part; size_t anon_len, prefix_len = 0; anon_part = strchr(url, '@'); if (url_is_local_not_ssh(url) || !anon_part) goto literal_copy; anon_len = strlen(++anon_part); scheme_prefix = strstr(url, "://"); if (!scheme_prefix) { if (!strchr(anon_part, ':')) /* cannot be "me@there:/path/name" */ goto literal_copy; } else { const char *cp; /* make sure scheme is reasonable */ for (cp = url; cp < scheme_prefix; cp++) { switch (*cp) { /* RFC 1738 2.1 */ case '+': case '.': case '-': break; /* ok */ default: if (isalnum(*cp)) break; /* it isn't */ goto literal_copy; } } /* @ past the first slash does not count */ cp = strchr(scheme_prefix + 3, '/'); if (cp && cp < anon_part) goto literal_copy; prefix_len = scheme_prefix - url + 3; } return xstrfmt("%.*s%.*s", (int)prefix_len, url, (int)anon_len, anon_part); literal_copy: return xstrdup(url); }
/** * Returns the default configured value for --rebase. It first looks for the * value of "branch.$curr_branch.rebase", where $curr_branch is the current * branch, and if HEAD is detached or the configuration key does not exist, * looks for the value of "pull.rebase". If both configuration keys do not * exist, returns REBASE_FALSE. */ static enum rebase_type config_get_rebase(void) { struct branch *curr_branch = branch_get("HEAD"); const char *value; if (curr_branch) { char *key = xstrfmt("branch.%s.rebase", curr_branch->name); if (!git_config_get_value(key, &value)) { enum rebase_type ret = parse_config_rebase(key, value, 1); free(key); return ret; } free(key); } if (!git_config_get_value("pull.rebase", &value)) return parse_config_rebase("pull.rebase", value, 1); return REBASE_FALSE; }
static enum protocol_allow_config get_protocol_config(const char *type) { char *key = xstrfmt("protocol.%s.allow", type); char *value; /* first check the per-protocol config */ if (!git_config_get_string(key, &value)) { enum protocol_allow_config ret = parse_protocol_config(key, value); free(key); free(value); return ret; } free(key); /* if defined, fallback to user-defined default for unknown protocols */ if (!git_config_get_string("protocol.allow", &value)) { enum protocol_allow_config ret = parse_protocol_config("protocol.allow", value); free(value); return ret; } /* fallback to built-in defaults */ /* known safe */ if (!strcmp(type, "http") || !strcmp(type, "https") || !strcmp(type, "git") || !strcmp(type, "ssh") || !strcmp(type, "file")) return PROTOCOL_ALLOW_ALWAYS; /* known scary; err on the side of caution */ if (!strcmp(type, "ext")) return PROTOCOL_ALLOW_NEVER; /* unknown; by default let them be used only directly by the user */ return PROTOCOL_ALLOW_USER_ONLY; }
static int remote_exists(const char *path) { char *url = xstrfmt("%s%s", repo->url, path); int ret; switch (http_get_strbuf(url, NULL, NULL)) { case HTTP_OK: ret = 1; break; case HTTP_MISSING_TARGET: ret = 0; break; case HTTP_ERROR: error("unable to access '%s': %s", url, curl_errorstr); /* fallthrough */ default: ret = -1; } free(url); return ret; }
void stop_progress_msg(struct progress **p_progress, const char *msg) { struct progress *progress = *p_progress; if (!progress) return; *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ char *buf; struct throughput *tp = progress->throughput; if (tp) { uint64_t now_ns = getnanotime(); unsigned int misecs, rate; misecs = ((now_ns - progress->start_ns) * 4398) >> 32; rate = tp->curr_total / (misecs ? misecs : 1); throughput_string(&tp->display, tp->curr_total, rate); } progress_update = 1; buf = xstrfmt(", %s.\n", msg); display(progress, progress->last_value, buf); free(buf); }
/* * Try to read the location of the git directory from the .git file, * return path to git directory if found. * * On failure, if return_error_code is not NULL, return_error_code * will be set to an error code and NULL will be returned. If * return_error_code is NULL the function will die instead (for most * cases). */ const char *read_gitfile_gently(const char *path, int *return_error_code) { const int max_file_size = 1 << 20; /* 1MB */ int error_code = 0; char *buf = NULL; char *dir = NULL; const char *slash; struct stat st; int fd; ssize_t len; if (stat(path, &st)) { error_code = READ_GITFILE_ERR_STAT_FAILED; goto cleanup_return; } if (!S_ISREG(st.st_mode)) { error_code = READ_GITFILE_ERR_NOT_A_FILE; goto cleanup_return; } if (st.st_size > max_file_size) { error_code = READ_GITFILE_ERR_TOO_LARGE; goto cleanup_return; } fd = open(path, O_RDONLY); if (fd < 0) { error_code = READ_GITFILE_ERR_OPEN_FAILED; goto cleanup_return; } buf = xmalloc(st.st_size + 1); len = read_in_full(fd, buf, st.st_size); close(fd); if (len != st.st_size) { error_code = READ_GITFILE_ERR_READ_FAILED; goto cleanup_return; } buf[len] = '\0'; if (!starts_with(buf, "gitdir: ")) { error_code = READ_GITFILE_ERR_INVALID_FORMAT; goto cleanup_return; } while (buf[len - 1] == '\n' || buf[len - 1] == '\r') len--; if (len < 9) { error_code = READ_GITFILE_ERR_NO_PATH; goto cleanup_return; } buf[len] = '\0'; dir = buf + 8; if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) { size_t pathlen = slash+1 - path; dir = xstrfmt("%.*s%.*s", (int)pathlen, path, (int)(len - 8), buf + 8); free(buf); buf = dir; } if (!is_git_directory(dir)) { error_code = READ_GITFILE_ERR_NOT_A_REPO; goto cleanup_return; } update_linked_gitdir(path, dir); path = real_path(dir); cleanup_return: if (return_error_code) *return_error_code = error_code; else if (error_code) { switch (error_code) { case READ_GITFILE_ERR_STAT_FAILED: case READ_GITFILE_ERR_NOT_A_FILE: /* non-fatal; follow return path */ break; case READ_GITFILE_ERR_OPEN_FAILED: die_errno("Error opening '%s'", path); case READ_GITFILE_ERR_TOO_LARGE: die("Too large to be a .git file: '%s'", path); case READ_GITFILE_ERR_READ_FAILED: die("Error reading %s", path); case READ_GITFILE_ERR_INVALID_FORMAT: die("Invalid gitfile format: %s", path); case READ_GITFILE_ERR_NO_PATH: die("No path in gitfile: %s", path); case READ_GITFILE_ERR_NOT_A_REPO: die("Not a git repository: %s", dir); default: assert(0); } } free(buf); return error_code ? NULL : path; }
static int try_parent_shorthands(const char *arg) { char *dotdot; unsigned char sha1[20]; struct commit *commit; struct commit_list *parents; int parent_number; int include_rev = 0; int include_parents = 0; int exclude_parent = 0; if ((dotdot = strstr(arg, "^!"))) { include_rev = 1; if (dotdot[2]) return 0; } else if ((dotdot = strstr(arg, "^@"))) { include_parents = 1; if (dotdot[2]) return 0; } else if ((dotdot = strstr(arg, "^-"))) { include_rev = 1; exclude_parent = 1; if (dotdot[2]) { char *end; exclude_parent = strtoul(dotdot + 2, &end, 10); if (*end != '\0' || !exclude_parent) return 0; } } else return 0; *dotdot = 0; if (get_sha1_committish(arg, sha1)) { *dotdot = '^'; return 0; } commit = lookup_commit_reference(sha1); if (exclude_parent && exclude_parent > commit_list_count(commit->parents)) { *dotdot = '^'; return 0; } if (include_rev) show_rev(NORMAL, sha1, arg); for (parents = commit->parents, parent_number = 1; parents; parents = parents->next, parent_number++) { char *name = NULL; if (exclude_parent && parent_number != exclude_parent) continue; if (symbolic) name = xstrfmt("%s^%d", arg, parent_number); show_rev(include_parents ? NORMAL : REVERSED, parents->item->object.oid.hash, name); free(name); } *dotdot = '^'; return 1; }
int walker_fetch(struct walker *walker, int targets, char **target, const char **write_ref, const char *write_ref_log_details) { struct strbuf refname = STRBUF_INIT; struct strbuf err = STRBUF_INIT; struct transaction *transaction = NULL; unsigned char *sha1 = xmalloc(targets * 20); char *msg = NULL; int i, ret = -1; save_commit_buffer = 0; if (write_ref) { transaction = transaction_begin(&err); if (!transaction) { error("%s", err.buf); goto done; } } if (!walker->get_recover) { for_each_ref(mark_complete, NULL); commit_list_sort_by_date(&complete); } for (i = 0; i < targets; i++) { if (interpret_target(walker, target[i], &sha1[20 * i])) { error("Could not interpret response from server '%s' as something to pull", target[i]); goto done; } if (process(walker, lookup_unknown_object(&sha1[20 * i]))) goto done; } if (loop(walker)) goto done; if (!write_ref) { ret = 0; goto done; } if (write_ref_log_details) { msg = xstrfmt("fetch from %s", write_ref_log_details); } else { msg = NULL; } for (i = 0; i < targets; i++) { if (!write_ref[i]) continue; strbuf_reset(&refname); strbuf_addf(&refname, "refs/%s", write_ref[i]); if (transaction_update_ref(transaction, refname.buf, &sha1[20 * i], NULL, 0, 0, msg ? msg : "fetch (unknown)", &err)) { error("%s", err.buf); goto done; } } if (transaction_commit(transaction, &err)) { error("%s", err.buf); goto done; } ret = 0; done: transaction_free(transaction); free(msg); free(sha1); strbuf_release(&err); strbuf_release(&refname); return ret; }
int cmd_stash(int argc, const char **argv, const char *prefix) { int i = -1; pid_t pid = getpid(); const char *index_file; struct argv_array args = ARGV_ARRAY_INIT; struct option options[] = { OPT_END() }; if (!use_builtin_stash()) { const char *path = mkpath("%s/git-legacy-stash", git_exec_path()); if (sane_execvp(path, (char **)argv) < 0) die_errno(_("could not exec %s"), path); else BUG("sane_execvp() returned???"); } prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); git_config(git_diff_basic_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); index_file = get_index_file(); strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, (uintmax_t)pid); if (!argc) return !!push_stash(0, NULL, prefix); else if (!strcmp(argv[0], "apply")) return !!apply_stash(argc, argv, prefix); else if (!strcmp(argv[0], "clear")) return !!clear_stash(argc, argv, prefix); else if (!strcmp(argv[0], "drop")) return !!drop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "pop")) return !!pop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "branch")) return !!branch_stash(argc, argv, prefix); else if (!strcmp(argv[0], "list")) return !!list_stash(argc, argv, prefix); else if (!strcmp(argv[0], "show")) return !!show_stash(argc, argv, prefix); else if (!strcmp(argv[0], "store")) return !!store_stash(argc, argv, prefix); else if (!strcmp(argv[0], "create")) return !!create_stash(argc, argv, prefix); else if (!strcmp(argv[0], "push")) return !!push_stash(argc, argv, prefix); else if (!strcmp(argv[0], "save")) return !!save_stash(argc, argv, prefix); else if (*argv[0] != '-') usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_usage, options); if (strcmp(argv[0], "-p")) { while (++i < argc && strcmp(argv[i], "--")) { /* * `akpqu` is a string which contains all short options, * except `-m` which is verified separately. */ if ((strlen(argv[i]) == 2) && *argv[i] == '-' && strchr("akpqu", argv[i][1])) continue; if (!strcmp(argv[i], "--all") || !strcmp(argv[i], "--keep-index") || !strcmp(argv[i], "--no-keep-index") || !strcmp(argv[i], "--patch") || !strcmp(argv[i], "--quiet") || !strcmp(argv[i], "--include-untracked")) continue; /* * `-m` and `--message=` are verified separately because * they need to be immediately followed by a string * (i.e.`-m"foobar"` or `--message="foobar"`). */ if (starts_with(argv[i], "-m") || starts_with(argv[i], "--message=")) continue; usage_with_options(git_stash_usage, options); } } argv_array_push(&args, "push"); argv_array_pushv(&args, argv); return !!push_stash(args.argc, args.argv, prefix); }
static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start, *ptr; size_t len; char *dir; /* * Skip scheme. */ start = strstr(repo, "://"); if (start == NULL) start = repo; else start += 3; /* * Skip authentication data. The stripping does happen * greedily, such that we strip up to the last '@' inside * the host part. */ for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) { if (*ptr == '@') start = ptr + 1; } /* * Strip trailing spaces, slashes and /.git */ while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; if (end - start > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { end -= 5; while (start < end && is_dir_sep(end[-1])) end--; } /* * Strip trailing port number if we've got only a * hostname (that is, there is no dir separator but a * colon). This check is required such that we do not * strip URI's like '/foo/bar:2222.git', which should * result in a dir '2222' being guessed due to backwards * compatibility. */ if (memchr(start, '/', end - start) == NULL && memchr(start, ':', end - start) != NULL) { ptr = end; while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':') ptr--; if (start < ptr && ptr[-1] == ':') end = ptr - 1; } /* * Find last component. To remain backwards compatible we * also regard colons as path separators, such that * cloning a repository 'foo:bar.git' would result in a * directory 'bar' being guessed. */ ptr = end; while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':') ptr--; start = ptr; /* * Strip .{bundle,git}. */ len = end - start; strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git"); if (!len || (len == 1 && *start == '/')) die(_("No directory name could be guessed.\n" "Please specify a directory on the command line")); if (is_bare) dir = xstrfmt("%.*s.git", (int)len, start); else dir = xstrndup(start, len); /* * Replace sequences of 'control' characters and whitespace * with one ascii space, remove leading and trailing spaces. */ if (*dir) { char *out = dir; int prev_space = 1 /* strip leading whitespace */; for (end = dir; *end; ++end) { char ch = *end; if ((unsigned char)ch < '\x20') ch = '\x20'; if (isspace(ch)) { if (prev_space) continue; prev_space = 1; } else prev_space = 0; *out++ = ch; } *out = '\0'; if (out > dir && prev_space) out[-1] = '\0'; } return dir; }