char *get_locked_file_path(struct lock_file *lk) { if (!lk->active) die("BUG: get_locked_file_path() called for unlocked object"); if (lk->filename.len <= LOCK_SUFFIX_LEN) die("BUG: get_locked_file_path() called for malformed lock object"); return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN); }
static struct passwd *getpw_str(const char *username, size_t len) { struct passwd *pw; char *username_z = xmemdupz(username, len); pw = getpwnam(username_z); free(username_z); return pw; }
int cmd_main(int argc, const char **argv) { char *method = getenv("REQUEST_METHOD"); char *dir; struct service_cmd *cmd = NULL; char *cmd_arg = NULL; int i; struct strbuf hdr = STRBUF_INIT; set_die_routine(die_webcgi); set_die_is_recursing_routine(die_webcgi_recursing); if (!method) die("No REQUEST_METHOD from server"); if (!strcmp(method, "HEAD")) method = "GET"; dir = getdir(); for (i = 0; i < ARRAY_SIZE(services); i++) { struct service_cmd *c = &services[i]; regex_t re; regmatch_t out[1]; if (regcomp(&re, c->pattern, REG_EXTENDED)) die("Bogus regex in service table: %s", c->pattern); if (!regexec(&re, dir, 1, out, 0)) { size_t n; if (strcmp(method, c->method)) return bad_request(&hdr, c); cmd = c; n = out[0].rm_eo - out[0].rm_so; cmd_arg = xmemdupz(dir + out[0].rm_so + 1, n - 1); dir[out[0].rm_so] = 0; break; } regfree(&re); } if (!cmd) not_found(&hdr, "Request not supported: '%s'", dir); setup_path(); if (!enter_repo(dir, 0)) not_found(&hdr, "Not a git repository: '%s'", dir); git_config(git_default_config, NULL); if (!getenv("GIT_HTTP_EXPORT_ALL") && access("git-daemon-export-ok", F_OK) ) not_found(&hdr, "Repository not exported: '%s'", dir); http_config(); max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER", max_request_buffer); cmd->imp(&hdr, cmd_arg); return 0; }
int parse_tag_buffer(struct tag *item, const void *data, unsigned long size) { struct object_id oid; char type[20]; const char *bufptr = data; const char *tail = bufptr + size; const char *nl; if (item->object.parsed) return 0; item->object.parsed = 1; if (size < GIT_SHA1_HEXSZ + 24) return -1; if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n') return -1; if (!starts_with(bufptr, "type ")) return -1; bufptr += 5; nl = memchr(bufptr, '\n', tail - bufptr); if (!nl || sizeof(type) <= (nl - bufptr)) return -1; memcpy(type, bufptr, nl - bufptr); type[nl - bufptr] = '\0'; bufptr = nl + 1; if (!strcmp(type, blob_type)) { item->tagged = (struct object *)lookup_blob(&oid); } else if (!strcmp(type, tree_type)) { item->tagged = (struct object *)lookup_tree(&oid); } else if (!strcmp(type, commit_type)) { item->tagged = (struct object *)lookup_commit(&oid); } else if (!strcmp(type, tag_type)) { item->tagged = (struct object *)lookup_tag(&oid); } else { error("Unknown type %s", type); item->tagged = NULL; } if (bufptr + 4 < tail && starts_with(bufptr, "tag ")) ; /* good */ else return -1; bufptr += 4; nl = memchr(bufptr, '\n', tail - bufptr); if (!nl) return -1; item->tag = xmemdupz(bufptr, nl - bufptr); bufptr = nl + 1; if (bufptr + 7 < tail && starts_with(bufptr, "tagger ")) item->date = parse_tag_date(bufptr, tail); else item->date = 0; return 0; }
int urlmatch_config_entry(const char *var, const char *value, void *cb) { struct string_list_item *item; struct urlmatch_config *collect = cb; struct urlmatch_item matched = {0}; struct url_info *url = &collect->url; const char *key, *dot; struct strbuf synthkey = STRBUF_INIT; int retval; if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') { if (collect->cascade_fn) return collect->cascade_fn(var, value, cb); return 0; /* not interested */ } dot = strrchr(key, '.'); if (dot) { char *config_url, *norm_url; struct url_info norm_info; config_url = xmemdupz(key, dot - key); norm_url = url_normalize_1(config_url, &norm_info, 1); free(config_url); if (!norm_url) return 0; retval = match_urls(url, &norm_info, &matched); free(norm_url); if (!retval) return 0; key = dot + 1; } if (collect->key && strcmp(key, collect->key)) return 0; item = string_list_insert(&collect->vars, key); if (!item->util) { item->util = xcalloc(1, sizeof(matched)); } else { if (cmp_matches(&matched, item->util) < 0) /* * Our match is worse than the old one, * we cannot use it. */ return 0; /* Otherwise, replace it with this one. */ } memcpy(item->util, &matched, sizeof(matched)); strbuf_addstr(&synthkey, collect->section); strbuf_addch(&synthkey, '.'); strbuf_addstr(&synthkey, key); retval = collect->collect_fn(synthkey.buf, value, collect->cb); strbuf_release(&synthkey); return retval; }
static char *ref_msg(const char *line, const char *endp) { const char *ep; line += 82; ep = memchr(line, '\n', endp - line); if (!ep) ep = endp; return xmemdupz(line, ep - line); }
bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) { if (obj->type != MSGPACK_OBJECT_RAW) { return false; } arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); arg->size = obj->via.raw.size; return true; }
static void prepare_order(const char *orderfile) { int fd, cnt, pass; void *map; char *cp, *endp; struct stat st; size_t sz; if (order) return; fd = open(orderfile, O_RDONLY); if (fd < 0) return; if (fstat(fd, &st)) { close(fd); return; } sz = xsize_t(st.st_size); map = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); close(fd); if (map == MAP_FAILED) return; endp = (char *) map + sz; for (pass = 0; pass < 2; pass++) { cnt = 0; cp = map; while (cp < endp) { char *ep; for (ep = cp; ep < endp && *ep != '\n'; ep++) ; /* cp to ep has one line */ if (*cp == '\n' || *cp == '#') ; /* comment */ else if (pass == 0) cnt++; else { if (*ep == '\n') { *ep = 0; order[cnt] = cp; } else { order[cnt] = xmemdupz(cp, ep - cp); } cnt++; } if (ep < endp) ep++; cp = ep; } if (pass == 0) { order_cnt = cnt; order = xmalloc(sizeof(*order) * cnt); } } }
/* * Used to parse format string and sort specifiers */ static int parse_atom(const char *atom, const char *ep) { const char *sp; int i, at; sp = atom; if (*sp == '*' && sp < ep) sp++; /* deref */ if (ep <= sp) die("malformed field name: %.*s", (int)(ep-atom), atom); /* Do we have the atom already used elsewhere? */ for (i = 0; i < used_atom_cnt; i++) { int len = strlen(used_atom[i]); if (len == ep - atom && !memcmp(used_atom[i], atom, len)) return i; } /* Is the atom a valid one? */ for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { int len = strlen(valid_atom[i].name); /* * If the atom name has a colon, strip it and everything after * it off - it specifies the format for this entry, and * shouldn't be used for checking against the valid_atom * table. */ const char *formatp = strchr(sp, ':'); if (!formatp || ep < formatp) formatp = ep; if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) break; } if (ARRAY_SIZE(valid_atom) <= i) die("unknown field name: %.*s", (int)(ep-atom), atom); /* Add it in, including the deref prefix */ at = used_atom_cnt; used_atom_cnt++; used_atom = xrealloc(used_atom, (sizeof *used_atom) * used_atom_cnt); used_atom_type = xrealloc(used_atom_type, (sizeof(*used_atom_type) * used_atom_cnt)); used_atom[at] = xmemdupz(atom, ep - atom); used_atom_type[at] = valid_atom[i].cmp_type; if (*atom == '*') need_tagged = 1; if (!strcmp(used_atom[at], "symref")) need_symref = 1; return at; }
static int nfvasprintf(char **strp, const char *fmt, va_list ap) { int len; char tmp[8192]; len = vsnprintf(tmp, sizeof(tmp), fmt, ap); if (len < 0) die("Fatal: Out of memory"); if (len >= sizeof(tmp)) die("imap command overflow!"); *strp = xmemdupz(tmp, len); return len; }
void credential_from_url(struct credential *c, const char *url) { const char *at, *colon, *cp, *slash, *host, *proto_end; credential_clear(c); /* * Match one of: * (1) proto://<host>/... * (2) proto://<user>@<host>/... * (3) proto://<user>:<pass>@<host>/... */ proto_end = strstr(url, "://"); if (!proto_end) return; cp = proto_end + 3; at = strchr(cp, '@'); colon = strchr(cp, ':'); slash = strchrnul(cp, '/'); if (!at || slash <= at) { /* Case (1) */ host = cp; } else if (!colon || at <= colon) { /* Case (2) */ c->username = url_decode_mem(cp, at - cp); host = at + 1; } else { /* Case (3) */ c->username = url_decode_mem(cp, colon - cp); c->password = url_decode_mem(colon + 1, at - (colon + 1)); host = at + 1; } if (proto_end - url > 0) c->protocol = xmemdupz(url, proto_end - url); if (slash - host > 0) c->host = url_decode_mem(host, slash - host); /* Trim leading and trailing slashes from path */ while (*slash == '/') slash++; if (*slash) { char *p; c->path = url_decode(slash); p = c->path + strlen(c->path) - 1; while (p > c->path && *p == '/') *p-- = '\0'; } }
static struct grep_pat *create_grep_pat(const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t, enum grep_header_field field) { struct grep_pat *p = xcalloc(1, sizeof(*p)); p->pattern = xmemdupz(pat, patlen); p->patternlen = patlen; p->origin = origin; p->no = no; p->token = t; p->field = field; return p; }
static void set_author_ident_env(const char *message) { const char *p = message; if (!p) die ("Could not read commit message of %s", sha1_to_hex(commit->object.sha1)); while (*p && *p != '\n') { const char *eol; for (eol = p; *eol && *eol != '\n'; eol++) ; /* do nothing */ if (!prefixcmp(p, "author ")) { char *line, *pend, *email, *timestamp; p += 7; line = xmemdupz(p, eol - p); email = strchr(line, '<'); if (!email) die ("Could not extract author email from %s", sha1_to_hex(commit->object.sha1)); if (email == line) pend = line; else for (pend = email; pend != line + 1 && isspace(pend[-1]); pend--); ; /* do nothing */ *pend = '\0'; email++; timestamp = strchr(email, '>'); if (!timestamp) die ("Could not extract author time from %s", sha1_to_hex(commit->object.sha1)); *timestamp = '\0'; for (timestamp++; *timestamp && isspace(*timestamp); timestamp++) ; /* do nothing */ setenv("GIT_AUTHOR_NAME", line, 1); setenv("GIT_AUTHOR_EMAIL", email, 1); setenv("GIT_AUTHOR_DATE", timestamp, 1); free(line); return; } p = eol; if (*p == '\n') p++; } die ("No author information found in %s", sha1_to_hex(commit->object.sha1)); }
static int git_proxy_command_options(const char *var, const char *value, void *cb) { if (!strcmp(var, "core.gitproxy")) { const char *for_pos; int matchlen = -1; int hostlen; const char *rhost_name = cb; int rhost_len = strlen(rhost_name); if (git_proxy_command) return 0; if (!value) return config_error_nonbool(var); /* [core] * ;# matches www.kernel.org as well * gitproxy = netcatter-1 for kernel.org * gitproxy = netcatter-2 for sample.xz * gitproxy = netcatter-default */ for_pos = strstr(value, " for "); if (!for_pos) /* matches everybody */ matchlen = strlen(value); else { hostlen = strlen(for_pos + 5); if (rhost_len < hostlen) matchlen = -1; else if (!strncmp(for_pos + 5, rhost_name + rhost_len - hostlen, hostlen) && ((rhost_len == hostlen) || rhost_name[rhost_len - hostlen -1] == '.')) matchlen = for_pos - value; else matchlen = -1; } if (0 <= matchlen) { /* core.gitproxy = none for kernel.org */ if (matchlen == 4 && !memcmp(value, "none", 4)) matchlen = 0; git_proxy_command = xmemdupz(value, matchlen); } return 0; } return git_default_config(var, value, cb); }
static void read_mailmap_buf(struct string_list *map, const char *buf, unsigned long len, char **repo_abbrev) { while (len) { const char *end = strchrnul(buf, '\n'); unsigned long linelen = end - buf + 1; char *line = xmemdupz(buf, linelen); read_mailmap_line(map, line, repo_abbrev); free(line); buf += linelen; len -= linelen; } }
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 parse_signature_lines(struct format_commit_context *ctx) { const char *buf = ctx->signature.gpg_output; int i; for (i = 0; i < ARRAY_SIZE(signature_check); i++) { const char *found = strstr(buf, signature_check[i].check); const char *next; if (!found) continue; ctx->signature.good_bad = signature_check[i].result; found += strlen(signature_check[i].check); next = strchrnul(found, '\n'); ctx->signature.signer = xmemdupz(found, next - found); break; } }
static int tar_filter_config(const char *var, const char *value, void *data) { struct archiver *ar; const char *dot; const char *name; const char *type; int namelen; if (prefixcmp(var, "tar.")) return 0; dot = strrchr(var, '.'); if (dot == var + 9) return 0; name = var + 4; namelen = dot - name; type = dot + 1; ar = find_tar_filter(name, namelen); if (!ar) { ar = xcalloc(1, sizeof(*ar)); ar->name = xmemdupz(name, namelen); ar->write_archive = write_tar_filter_archive; ar->flags = ARCHIVER_WANT_COMPRESSION_LEVELS; ALLOC_GROW(tar_filters, nr_tar_filters + 1, alloc_tar_filters); tar_filters[nr_tar_filters++] = ar; } if (!strcmp(type, "command")) { if (!value) return config_error_nonbool(var); free(ar->data); ar->data = xstrdup(value); return 0; } if (!strcmp(type, "remote")) { if (git_config_bool(var, value)) ar->flags |= ARCHIVER_REMOTE; else ar->flags &= ~ARCHIVER_REMOTE; return 0; } return 0; }
/* * Parse a whitespace-delimited attribute state (i.e., "attr", * "-attr", "!attr", or "attr=value") from the string starting at src. * If e is not NULL, write the results to *e. Return a pointer to the * remainder of the string (with leading whitespace removed), or NULL * if there was an error. */ static const char *parse_attr(const char *src, int lineno, const char *cp, struct attr_state *e) { const char *ep, *equals; int len; ep = cp + strcspn(cp, blank); equals = strchr(cp, '='); if (equals && ep < equals) equals = NULL; if (equals) len = equals - cp; else len = ep - cp; if (!e) { if (*cp == '-' || *cp == '!') { cp++; len--; } if (!attr_name_valid(cp, len)) { report_invalid_attr(cp, len, src, lineno); return NULL; } } else { /* * As this function is always called twice, once with * e == NULL in the first pass and then e != NULL in * the second pass, no need for attr_name_valid() * check here. */ if (*cp == '-' || *cp == '!') { e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET; cp++; len--; } else if (!equals) e->setto = ATTR__TRUE; else { e->setto = xmemdupz(equals + 1, ep - equals - 1); } e->attr = git_attr_internal(cp, len); } return ep + strspn(ep, blank); }
int fill_directory(struct dir_struct *dir, const char **pathspec) { const char *path; int len; /* * Calculate common prefix for the pathspec, and * use that to optimize the directory walk */ len = common_prefix(pathspec); path = ""; if (len) path = xmemdupz(*pathspec, len); /* Read the directory and prune it */ read_directory(dir, path, len, pathspec); return len; }
/* * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing * after dropping "_commit" from its name and possibly moving it out * of commit.c */ static char *find_header(const char *msg, size_t len, const char *key) { int key_len = strlen(key); const char *line = msg; while (line && line < msg + len) { const char *eol = strchrnul(line, '\n'); if ((msg + len <= eol) || line == eol) return NULL; if (line + key_len < eol && !memcmp(line, key, key_len) && line[key_len] == ' ') { int offset = key_len + 1; return xmemdupz(line + offset, (eol - line) - offset); } line = *eol ? eol + 1 : NULL; } return NULL; }
static int credential_config_callback(const char *var, const char *value, void *data) { struct credential *c = data; const char *key, *dot; if (!skip_prefix(var, "credential.", &key)) return 0; if (!value) return config_error_nonbool(var); dot = strrchr(key, '.'); if (dot) { struct credential want = CREDENTIAL_INIT; char *url = xmemdupz(key, dot - key); int matched; credential_from_url(&want, url); matched = credential_match(&want, c); credential_clear(&want); free(url); if (!matched) return 0; key = dot + 1; } if (!strcmp(key, "helper")) { if (*value) string_list_append(&c->helpers, value); else string_list_clear(&c->helpers, 0); } else if (!strcmp(key, "username")) { if (!c->username) c->username = xstrdup(value); } else if (!strcmp(key, "usehttppath")) c->use_http_path = git_config_bool(var, value); return 0; }
/// Msgpack callback for writing to readfile()-style list int encode_list_write(void *data, const char *buf, size_t len) { if (len == 0) { return 0; } list_T *const list = (list_T *) data; const char *const end = buf + len; const char *line_end = buf; listitem_T *li = list->lv_last; // Continue the last list element if (li != NULL) { line_end = xmemscan(buf, NL, len); if (line_end != buf) { const size_t line_length = (size_t)(line_end - buf); char *str = (char *)li->li_tv.vval.v_string; const size_t li_len = (str == NULL ? 0 : strlen(str)); li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); str = (char *)li->li_tv.vval.v_string + li_len; memcpy(str, buf, line_length); str[line_length] = 0; memchrsub(str, NUL, NL, line_length); } line_end++; } while (line_end < end) { const char *line_start = line_end; line_end = xmemscan(line_start, NL, (size_t) (end - line_start)); char *str = NULL; if (line_end != line_start) { const size_t line_length = (size_t)(line_end - line_start); str = xmemdupz(line_start, line_length); memchrsub(str, NUL, NL, line_length); } list_append_allocated_string(list, str); line_end++; } if (line_end == end) { list_append_allocated_string(list, NULL); } return 0; }
static const char *clean_message_id(const char *msg_id) { char ch; const char *a, *z, *m; m = msg_id; while ((ch = *m) && (isspace(ch) || (ch == '<'))) m++; a = m; z = NULL; while ((ch = *m)) { if (!isspace(ch) && (ch != '>')) z = m; m++; } if (!z) die("insane in-reply-to: %s", msg_id); if (++z == m) return a; return xmemdupz(a, z - a); }
static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) { struct fsck_gitmodules_data *data = vdata; const char *subsection, *key; int subsection_len; char *name; if (parse_config_key(var, "submodule", &subsection, &subsection_len, &key) < 0 || !subsection) return 0; name = xmemdupz(subsection, subsection_len); if (check_submodule_name(name) < 0) data->ret |= report(data->options, data->obj, FSCK_MSG_GITMODULES_NAME, "disallowed submodule name: %s", name); free(name); return 0; }
static char *get_header(const struct commit *commit, const char *key) { int key_len = strlen(key); const char *line = commit->buffer; for (;;) { const char *eol = strchr(line, '\n'), *next; if (line == eol) return NULL; if (!eol) { eol = line + strlen(line); next = NULL; } else next = eol + 1; if (eol - line > key_len && !strncmp(line, key, key_len) && line[key_len] == ' ') { return xmemdupz(line + key_len + 1, eol - line - key_len - 1); } line = next; } }
/* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. */ static int verify_absent_1(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { int len; struct stat st; if (o->index_only || o->reset || !o->update) return 0; len = check_leading_path(ce->name, ce_namelen(ce)); if (!len) return 0; else if (len > 0) { char *path; int ret; path = xmemdupz(ce->name, len); if (lstat(path, &st)) ret = error("cannot stat '%s': %s", path, strerror(errno)); else ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st, error_type, o); free(path); return ret; } else if (lstat(ce->name, &st)) { if (errno != ENOENT) return error("cannot stat '%s': %s", ce->name, strerror(errno)); return 0; } else { return check_ok_to_remove(ce->name, ce_namelen(ce), ce_to_dtype(ce), ce, &st, error_type, o); } }
int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc) { struct strbuf gpg_output = STRBUF_INIT; struct strbuf gpg_status = STRBUF_INIT; int status; sigc->result = 'N'; status = verify_signed_buffer(payload, plen, signature, slen, &gpg_output, &gpg_status); if (status && !gpg_output.len) goto out; sigc->payload = xmemdupz(payload, plen); sigc->gpg_output = strbuf_detach(&gpg_output, NULL); sigc->gpg_status = strbuf_detach(&gpg_status, NULL); parse_gpg_output(sigc); out: strbuf_release(&gpg_status); strbuf_release(&gpg_output); return sigc->result != 'G' && sigc->result != 'U'; }
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; int nr = 0, total, i; int use_stdout = 0; int start_number = -1; int numbered_files = 0; /* _just_ numbers */ int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, "use [PATCH n/m] even with a single patch", PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, "use [PATCH] even with multiple patches", PARSE_OPT_NOARG, no_numbered_callback }, OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"), OPT_BOOLEAN(0, "stdout", &use_stdout, "print patches to standard out"), OPT_BOOLEAN(0, "cover-letter", &cover_letter, "generate a cover letter"), OPT_BOOLEAN(0, "numbered-files", &numbered_files, "use simple number sequence for output file names"), OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx", "use <sfx> instead of '.patch'"), OPT_INTEGER(0, "start-number", &start_number, "start numbering patches at <n> instead of 1"), { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix", "Use [<prefix>] instead of [PATCH]", PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, "dir", "store resulting files in <dir>", PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, "don't strip/add [PATCH]", PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, "don't output binary diffs"), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, "don't include a patch matching a commit upstream"), { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, "show patch format instead of default (patch + stat)", PARSE_OPT_NONEG | PARSE_OPT_NOARG }, OPT_GROUP("Messaging"), { OPTION_CALLBACK, 0, "add-header", NULL, "header", "add email header", PARSE_OPT_NONEG, header_callback }, { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", PARSE_OPT_NONEG, cc_callback }, OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id", "make first mail a reply to <message-id>"), { OPTION_CALLBACK, 0, "attach", &rev, "boundary", "attach the patch", PARSE_OPT_OPTARG, attach_callback }, { OPTION_CALLBACK, 0, "inline", &rev, "boundary", "inline the patch", PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, { OPTION_CALLBACK, 0, "thread", &thread, "style", "enable message threading, styles: shallow, deep", PARSE_OPT_OPTARG, thread_callback }, OPT_END() }; git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.combine_merges = 0; rev.ignore_merges = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; } /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (do_signoff) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogus committer info %s", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } for (i = 0; i < extra_hdr_nr; i++) { strbuf_addstr(&buf, extra_hdr[i]); strbuf_addch(&buf, '\n'); } if (extra_to_nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to[i]); if (i + 1 < extra_to_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc_nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc[i]); if (i + 1 < extra_cc_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, NULL); if (start_number < 0) start_number = 1; /* * If numbered is set solely due to format.numbered in config, * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ if (numbered && keep_subject && !numbered_cmdline_opt) numbered = 0; if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) die ("--subject-prefix and -k are mutually exclusive."); argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die("--name-only does not make sense"); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) die("--name-status does not make sense"); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) die("--check does not make sense"); if (!use_patch_format && (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); if (output_directory) { if (use_stdout) die("standard output, or directory, which one?"); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die_errno("Could not create directory '%s'", output_directory); } if (rev.pending.nr == 1) { if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ } /* * We cannot move this anywhere earlier because we do want to * know if --root was given explicitly from the comand line. */ rev.show_root_diff = 1; if (cover_letter) { /* remember the range */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } /* We can't generate a cover letter without any patches */ if (!head) return 0; } if (ignore_if_in_upstream) get_patch_ids(&rev, &ids, prefix); if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } /* ignore merges */ if (commit->parents && commit->parents->next) continue; if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; list = xrealloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { const char *msgid = clean_message_id(in_reply_to); string_list_append(msgid, rev.ref_message_ids); } rev.numbered_files = numbered_files; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, origin, nr, list, head); total++; start_number--; } rev.add_signoff = add_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * For deep threading: make every mail * a reply to the previous one, no * matter what other options are set. * * For shallow threading: * * Without --cover-letter and * --in-reply-to, make every mail a * reply to the one before. * * With --in-reply-to but no * --cover-letter, make every mail a * reply to the <reply-to>. * * With --cover-letter, make every * mail but the cover letter a reply * to the cover letter. The cover * letter is a reply to the * --in-reply-to, if specified. */ if (thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); else string_list_append(rev.message_id, rev.ref_message_ids); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, &rev)) die("Failed to create output files"); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else printf("-- \n%s\n\n", git_version_string); } if (!use_stdout) fclose(stdout); } free(list); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
static int handle_line(char *line, struct merge_parents *merge_parents) { int i, len = strlen(line); struct origin_data *origin_data; char *src, *origin; struct src_data *src_data; struct string_list_item *item; int pulling_head = 0; unsigned char sha1[20]; if (len < 43 || line[40] != '\t') return 1; if (!prefixcmp(line + 41, "not-for-merge")) return 0; if (line[41] != '\t') return 2; i = get_sha1_hex(line, sha1); if (i) return 3; if (!find_merge_parent(merge_parents, sha1, NULL)) return 0; /* subsumed by other parents */ origin_data = xcalloc(1, sizeof(struct origin_data)); hashcpy(origin_data->sha1, sha1); if (line[len - 1] == '\n') line[len - 1] = 0; line += 42; /* * At this point, line points at the beginning of comment e.g. * "branch 'frotz' of git://that/repository.git". * Find the repository name and point it with src. */ src = strstr(line, " of "); if (src) { *src = 0; src += 4; pulling_head = 0; } else { src = line; pulling_head = 1; } item = unsorted_string_list_lookup(&srcs, src); if (!item) { item = string_list_append(&srcs, src); item->util = xcalloc(1, sizeof(struct src_data)); init_src_data(item->util); } src_data = item->util; if (pulling_head) { origin = src; src_data->head_status |= 1; } else if (!prefixcmp(line, "branch ")) { origin_data->is_local_branch = 1; origin = line + 7; string_list_append(&src_data->branch, origin); src_data->head_status |= 2; } else if (!prefixcmp(line, "tag ")) { origin = line; string_list_append(&src_data->tag, origin + 4); src_data->head_status |= 2; } else if (!prefixcmp(line, "remote-tracking branch ")) { origin = line + strlen("remote-tracking branch "); string_list_append(&src_data->r_branch, origin); src_data->head_status |= 2; } else { origin = src; string_list_append(&src_data->generic, line); src_data->head_status |= 2; } if (!strcmp(".", src) || !strcmp(src, origin)) { int len = strlen(origin); if (origin[0] == '\'' && origin[len - 1] == '\'') origin = xmemdupz(origin + 1, len - 2); } else { char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5); sprintf(new_origin, "%s of %s", origin, src); origin = new_origin; } if (strcmp(".", src)) origin_data->is_local_branch = 0; string_list_append(&origins, origin)->util = origin_data; return 0; }