static int handle_config(const char *key, const char *value, void *cb) { const char *name; const char *subkey; struct remote *remote; struct branch *branch; if (!prefixcmp(key, "branch.")) { name = key + 7; subkey = strrchr(name, '.'); if (!subkey) return 0; branch = make_branch(name, subkey - name); if (!strcmp(subkey, ".remote")) { if (!value) return config_error_nonbool(key); branch->remote_name = xstrdup(value); if (branch == current_branch) default_remote_name = branch->remote_name; } else if (!strcmp(subkey, ".merge")) { if (!value) return config_error_nonbool(key); add_merge(branch, xstrdup(value)); } return 0; } if (!prefixcmp(key, "url.")) { struct rewrite *rewrite; name = key + 4; subkey = strrchr(name, '.'); if (!subkey) return 0; rewrite = make_rewrite(name, subkey - name); if (!strcmp(subkey, ".insteadof")) { if (!value) return config_error_nonbool(key); add_instead_of(rewrite, xstrdup(value)); } } if (prefixcmp(key, "remote.")) return 0; name = key + 7; if (*name == '/') { warning("Config remote shorthand cannot begin with '/': %s", name); return 0; } subkey = strrchr(name, '.'); if (!subkey) return error("Config with no key for remote %s", name); remote = make_remote(name, subkey - name); remote->origin = REMOTE_CONFIG; if (!strcmp(subkey, ".mirror")) remote->mirror = git_config_bool(key, value); else if (!strcmp(subkey, ".skipdefaultupdate")) remote->skip_default_update = git_config_bool(key, value); else if (!strcmp(subkey, ".url")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_url(remote, v); } else if (!strcmp(subkey, ".push")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_push_refspec(remote, v); } else if (!strcmp(subkey, ".fetch")) { const char *v; if (git_config_string(&v, key, value)) return -1; add_fetch_refspec(remote, v); } else if (!strcmp(subkey, ".receivepack")) { const char *v; if (git_config_string(&v, key, value)) return -1; if (!remote->receivepack) remote->receivepack = v; else error("more than one receivepack given, using the first"); } else if (!strcmp(subkey, ".uploadpack")) { const char *v; if (git_config_string(&v, key, value)) return -1; if (!remote->uploadpack) remote->uploadpack = v; else error("more than one uploadpack given, using the first"); } else if (!strcmp(subkey, ".tagopt")) { if (!strcmp(value, "--no-tags")) remote->fetch_tags = -1; } else if (!strcmp(subkey, ".proxy")) { return git_config_string((const char **)&remote->http_proxy, key, value); } return 0; }
void wt_status_print(struct wt_status *s) { const char *branch_color = color(WT_STATUS_HEADER, s); if (s->branch) { const char *on_what = "On branch "; const char *branch_name = s->branch; if (!prefixcmp(branch_name, "refs/heads/")) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = ""; branch_color = color(WT_STATUS_NOBRANCH, s); on_what = "Not currently on any branch."; } color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# "); color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name); if (!s->is_initial) wt_status_print_tracking(s); } if (s->is_initial) { color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit"); color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); } wt_status_print_updated(s); wt_status_print_unmerged(s); wt_status_print_changed(s); if (s->submodule_summary && (!s->ignore_submodule_arg || strcmp(s->ignore_submodule_arg, "all"))) { wt_status_print_submodule_summary(s, 0); /* staged */ wt_status_print_submodule_summary(s, 1); /* unstaged */ } if (s->show_untracked_files) { wt_status_print_other(s, &s->untracked, "Untracked", "add"); if (s->show_ignored_files) wt_status_print_other(s, &s->ignored, "Ignored", "add -f"); } else if (s->commitable) fprintf(s->fp, "# Untracked files not listed%s\n", advice_status_hints ? " (use -u option to show untracked files)" : ""); if (s->verbose) wt_status_print_verbose(s); if (!s->commitable) { if (s->amend) fprintf(s->fp, "# No changes\n"); else if (s->nowarn) ; /* nothing */ else if (s->workdir_dirty) printf("no changes added to commit%s\n", advice_status_hints ? " (use \"git add\" and/or \"git commit -a\")" : ""); else if (s->untracked.nr) printf("nothing added to commit but untracked files present%s\n", advice_status_hints ? " (use \"git add\" to track)" : ""); else if (s->is_initial) printf("nothing to commit%s\n", advice_status_hints ? " (create/copy files and use \"git add\" to track)" : ""); else if (!s->show_untracked_files) printf("nothing to commit%s\n", advice_status_hints ? " (use -u to show untracked files)" : ""); else printf("nothing to commit%s\n", advice_status_hints ? " (working directory clean)" : ""); } }
/* Wants: mode request key */ static enum status_code option_bind_command(int argc, const char *argv[]) { struct key key[1]; size_t keys = 0; enum request request; struct keymap *keymap; const char *key_arg; if (argc < 3) return error("Invalid key binding: bind keymap key action"); if (!(keymap = get_keymap(argv[0], strlen(argv[0])))) { if (!strcmp(argv[0], "branch")) keymap = get_keymap("refs", strlen("refs")); if (!keymap) return error("Unknown key map: %s", argv[0]); } for (keys = 0, key_arg = argv[1]; *key_arg && keys < ARRAY_SIZE(key); keys++) { enum status_code code = get_key_value(&key_arg, &key[keys]); if (code != SUCCESS) return code; } if (*key_arg && keys == ARRAY_SIZE(key)) return error("Except for <Esc> combos only one key is allowed " "in key combos: %s", argv[1]); request = get_request(argv[2]); if (request == REQ_UNKNOWN) { static const char *obsolete[][2] = { { "view-branch", "view-refs" }, }; static const char *toggles[][2] = { { "diff-context-down", "diff-context" }, { "diff-context-up", "diff-context" }, { "stage-next", ":/^@@" }, { "toggle-author", "author" }, { "toggle-changes", "show-changes" }, { "toggle-commit-order", "show-commit-order" }, { "toggle-date", "date" }, { "toggle-files", "file-filter" }, { "toggle-file-filter", "file-filter" }, { "toggle-file-size", "file-size" }, { "toggle-filename", "filename" }, { "toggle-graphic", "show-graphic" }, { "toggle-id", "id" }, { "toggle-ignore-space", "show-ignore-space" }, { "toggle-lineno", "line-number" }, { "toggle-refs", "commit-title-refs" }, { "toggle-rev-graph", "commit-title-graph" }, { "toggle-show-changes", "show-changes" }, { "toggle-sort-field", "sort-field" }, { "toggle-sort-order", "sort-order" }, { "toggle-title-overflow", "commit-title-overflow" }, { "toggle-untracked-dirs", "status-untracked-dirs" }, { "toggle-vertical-split", "show-vertical-split" }, }; int alias; alias = find_remapped(obsolete, ARRAY_SIZE(obsolete), argv[2]); if (alias != -1) { const char *action = obsolete[alias][1]; add_keybinding(keymap, get_request(action), key, keys); return error("%s has been renamed to %s", obsolete[alias][0], action); } alias = find_remapped(toggles, ARRAY_SIZE(toggles), argv[2]); if (alias != -1) { const char *action = toggles[alias][0]; const char *arg = prefixcmp(action, "diff-context-") ? NULL : (strstr(action, "-down") ? "-1" : "+1"); const char *mapped = toggles[alias][1]; const char *toggle[] = { ":toggle", mapped, arg, NULL}; const char *other[] = { mapped, NULL }; const char **prompt = *mapped == ':' ? other : toggle; enum status_code code = add_run_request(keymap, key, keys, prompt); if (code == SUCCESS) code = error("%s has been replaced by `%s%s%s%s'", action, prompt == other ? mapped : ":toggle ", prompt == other ? "" : mapped, arg ? " " : "", arg ? arg : ""); return code; } } if (request == REQ_UNKNOWN) return add_run_request(keymap, key, keys, argv + 2); return add_keybinding(keymap, request, key, keys); }
static int fetch_indices(struct walker *walker, struct alt_base *repo) { unsigned char sha1[20]; char *url; struct strbuf buffer = STRBUF_INIT; char *data; int i = 0; int ret = 0; struct active_request_slot *slot; struct slot_results results; if (repo->got_indices) return 0; if (walker->get_verbosely) fprintf(stderr, "Getting pack list for %s\n", repo->base); url = xmalloc(strlen(repo->base) + 21); sprintf(url, "%s/objects/info/packs", repo->base); slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); if (start_active_slot(slot)) { run_active_slot(slot); if (results.curl_result != CURLE_OK) { if (missing_target(&results)) { repo->got_indices = 1; goto cleanup; } else { repo->got_indices = 0; ret = error("%s", curl_errorstr); goto cleanup; } } } else { repo->got_indices = 0; ret = error("Unable to start request"); goto cleanup; } data = buffer.buf; while (i < buffer.len) { switch (data[i]) { case 'P': i++; if (i + 52 <= buffer.len && !prefixcmp(data + i, " pack-") && !prefixcmp(data + i + 46, ".pack\n")) { get_sha1_hex(data + i + 6, sha1); setup_index(walker, repo, sha1); i += 51; break; } default: while (i < buffer.len && data[i] != '\n') i++; } i++; } repo->got_indices = 1; cleanup: strbuf_release(&buffer); free(url); return ret; }
int main(int argc, const char **argv) { const char *cmd; startup_info = &git_startup_info; cmd = git_extract_argv0_path(argv[0]); if (!cmd) cmd = "git-help"; git_setup_gettext(); /* * "git-xxxx" is the same as "git xxxx", but we obviously: * * - cannot take flags in between the "git" and the "xxxx". * - cannot execute it externally (since it would just do * the same thing over again) * * So we just directly call the internal command handler, and * die if that one cannot handle it. */ if (!prefixcmp(cmd, "git-")) { cmd += 4; argv[0] = cmd; handle_internal_command(argc, argv); die("cannot handle %s internally", cmd); } /* Look for flags.. */ argv++; argc--; handle_options(&argv, &argc, NULL); if (argc > 0) { if (!prefixcmp(argv[0], "--")) argv[0] += 2; } else { /* The user didn't specify a command; give them help */ commit_pager_choice(); printf("usage: %s\n\n", git_usage_string); list_common_cmds_help(); printf("\n%s\n", _(git_more_info_string)); exit(1); } cmd = argv[0]; /* * We use PATH to find git commands, but we prepend some higher * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH * environment, and the $(gitexecdir) from the Makefile at build * time. */ setup_path(); while (1) { static int done_help = 0; static int was_alias = 0; was_alias = run_argv(&argc, &argv); if (errno != ENOENT) break; if (was_alias) { fprintf(stderr, "Expansion of alias '%s' failed; " "'%s' is not a git command\n", cmd, argv[0]); exit(1); } if (!done_help) { cmd = argv[0] = help_unknown_cmd(cmd); done_help = 1; } else break; } fprintf(stderr, "Failed to run command '%s': %s\n", cmd, strerror(errno)); return 1; }
static int perform_http_xact(void) { /* use free instead of g_free so that we can use xstr* functions from * libreport/lib/xfuncs.c */ GHashTable *problem_info = g_hash_table_new_full(g_str_hash, g_str_equal, free, free); /* Read header */ char *body_start = NULL; char *messagebuf_data = NULL; unsigned messagebuf_len = 0; /* Loop until EOF/error/timeout/end_of_header */ while (1) { messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE); char *p = messagebuf_data + messagebuf_len; int rd = read(STDIN_FILENO, p, INPUT_BUFFER_SIZE); if (rd < 0) { if (errno == EINTR) /* SIGALRM? */ error_msg_and_die("Timed out"); perror_msg_and_die("read"); } if (rd == 0) break; log_debug("Received %u bytes of data", rd); messagebuf_len += rd; total_bytes_read += rd; if (total_bytes_read > MAX_MESSAGE_SIZE) error_msg_and_die("Message is too long, aborting"); /* Check whether we see end of header */ /* Note: we support both [\r]\n\r\n and \n\n */ char *past_end = messagebuf_data + messagebuf_len; if (p > messagebuf_data+1) p -= 2; /* start search from two last bytes in last read - they might be '\n\r' */ while (p < past_end) { p = memchr(p, '\n', past_end - p); if (!p) break; p++; if (p >= past_end) break; if (*p == '\n' || (*p == '\r' && p+1 < past_end && p[1] == '\n') ) { body_start = p + 1 + (*p == '\r'); *p = '\0'; goto found_end_of_header; } } } /* while (read) */ found_end_of_header: ; log_debug("Request: %s", messagebuf_data); /* Sanitize and analyze header. * Header now is in messagebuf_data, NUL terminated string, * with last empty line deleted (by placement of NUL). * \r\n are not (yet) converted to \n, multi-line headers also * not converted. */ /* First line must be "op<space>[http://host]/path<space>HTTP/n.n". * <space> is exactly one space char. */ if (prefixcmp(messagebuf_data, "DELETE ") == 0) { messagebuf_data += strlen("DELETE "); char *space = strchr(messagebuf_data, ' '); if (!space || prefixcmp(space+1, "HTTP/") != 0) return 400; /* Bad Request */ *space = '\0'; //decode_url(messagebuf_data); %20 => ' ' alarm(0); return delete_path(messagebuf_data); } /* We erroneously used "PUT /" to create new problems. * POST is the correct request in this case: * "PUT /" implies creation or replace of resource named "/"! * Delete PUT in 2014. */ if (prefixcmp(messagebuf_data, "PUT ") != 0 && prefixcmp(messagebuf_data, "POST ") != 0 ) { return 400; /* Bad Request */ } enum { CREATION_NOTIFICATION, CREATION_REQUEST, }; int url_type; char *url = skip_non_whitespace(messagebuf_data) + 1; /* skip "POST " */ if (prefixcmp(url, "/creation_notification ") == 0) url_type = CREATION_NOTIFICATION; else if (prefixcmp(url, "/ ") == 0) url_type = CREATION_REQUEST; else return 400; /* Bad Request */ /* Read body */ if (!body_start) { log_warning("Premature EOF detected, exiting"); return 400; /* Bad Request */ } messagebuf_len -= (body_start - messagebuf_data); memmove(messagebuf_data, body_start, messagebuf_len); log_debug("Body so far: %u bytes, '%s'", messagebuf_len, messagebuf_data); /* Loop until EOF/error/timeout */ while (1) { if (url_type == CREATION_REQUEST) { while (1) { unsigned len = strnlen(messagebuf_data, messagebuf_len); if (len >= messagebuf_len) break; /* messagebuf has at least one NUL - process the line */ process_message(problem_info, messagebuf_data); messagebuf_len -= (len + 1); memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len); } } messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE + 1); int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE); if (rd < 0) { if (errno == EINTR) /* SIGALRM? */ error_msg_and_die("Timed out"); perror_msg_and_die("read"); } if (rd == 0) break; log_debug("Received %u bytes of data", rd); messagebuf_len += rd; total_bytes_read += rd; if (total_bytes_read > MAX_MESSAGE_SIZE) error_msg_and_die("Message is too long, aborting"); } /* Body received, EOF was seen. Don't let alarm to interrupt after this. */ alarm(0); if (url_type == CREATION_NOTIFICATION) { messagebuf_data[messagebuf_len] = '\0'; return run_post_create(messagebuf_data); } /* Save problem dir */ int ret = 0; unsigned pid = convert_pid(problem_info); die_if_data_is_missing(problem_info); char *executable = g_hash_table_lookup(problem_info, FILENAME_EXECUTABLE); if (executable) { char *last_file = concat_path_file(g_settings_dump_location, "last-via-server"); int repeating_crash = check_recent_crash_file(last_file, executable); free(last_file); if (repeating_crash) /* Only pretend that we saved it */ goto out; /* ret is 0: "success" */ } #if 0 //TODO: /* At least it should generate local problem identifier UUID */ problem_data_add_basics(problem_info); //...the problem being that problem_info here is not a problem_data_t! #endif create_problem_dir(problem_info, pid); /* does not return */ out: g_hash_table_destroy(problem_info); return ret; /* Used as HTTP response code */ }
static int store_updated_refs(const char *url, const char *remote_name, struct ref *ref_map) { FILE *fp; struct commit *commit; int url_len, i, note_len, shown_url = 0, rc = 0; char note[1024]; const char *what, *kind; struct ref *rm; char *filename = git_path("FETCH_HEAD"); fp = fopen(filename, "a"); if (!fp) return error("cannot open %s: %s\n", filename, strerror(errno)); for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; if (rm->peer_ref) { ref = xcalloc(1, sizeof(*ref) + strlen(rm->peer_ref->name) + 1); strcpy(ref->name, rm->peer_ref->name); hashcpy(ref->old_sha1, rm->peer_ref->old_sha1); hashcpy(ref->new_sha1, rm->old_sha1); ref->force = rm->peer_ref->force; } commit = lookup_commit_reference_gently(rm->old_sha1, 1); if (!commit) rm->merge = 0; if (!strcmp(rm->name, "HEAD")) { kind = ""; what = ""; } else if (!prefixcmp(rm->name, "refs/heads/")) { kind = "branch"; what = rm->name + 11; } else if (!prefixcmp(rm->name, "refs/tags/")) { kind = "tag"; what = rm->name + 10; } else if (!prefixcmp(rm->name, "refs/remotes/")) { kind = "remote branch"; what = rm->name + 13; } else { kind = ""; what = rm->name; } url_len = strlen(url); for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) ; url_len = i + 1; if (4 < i && !strncmp(".git", url + i - 3, 4)) url_len = i - 3; note_len = 0; if (*what) { if (*kind) note_len += sprintf(note + note_len, "%s ", kind); note_len += sprintf(note + note_len, "'%s' of ", what); } note_len += sprintf(note + note_len, "%.*s", url_len, url); fprintf(fp, "%s\t%s\t%s\n", sha1_to_hex(commit ? commit->object.sha1 : rm->old_sha1), rm->merge ? "" : "not-for-merge", note); if (ref) rc |= update_local_ref(ref, what, note); else sprintf(note, "* %-*s %-*s -> FETCH_HEAD", SUMMARY_WIDTH, *kind ? kind : "branch", REFCOL_WIDTH, *what ? what : "HEAD"); if (*note) { if (verbosity >= 0 && !shown_url) { fprintf(stderr, "From %.*s\n", url_len, url); shown_url = 1; } if (verbosity >= 0) fprintf(stderr, " %s\n", note); } } fclose(fp); if (rc & 2) error("some local refs could not be updated; try running\n" " 'git remote prune %s' to remove any old, conflicting " "branches", remote_name); return rc; }
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, const unsigned char *new_sha1, const char *msg) { int logfd, written, oflags = O_APPEND | O_WRONLY; unsigned maxlen, len; int msglen; char log_file[PATH_MAX]; char *logrec; const char *committer; if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name); if (log_all_ref_updates && (!prefixcmp(ref_name, "refs/heads/") || !prefixcmp(ref_name, "refs/remotes/") || !strcmp(ref_name, "HEAD"))) { if (safe_create_leading_directories(log_file) < 0) return error("unable to create directory for %s", log_file); oflags |= O_CREAT; } logfd = open(log_file, oflags, 0666); if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; if ((oflags & O_CREAT) && errno == EISDIR) { if (remove_empty_directories(log_file)) { return error("There are still logs under '%s'", log_file); } logfd = open(log_file, oflags, 0666); } if (logfd < 0) return error("Unable to append to %s: %s", log_file, strerror(errno)); } adjust_shared_perm(log_file); msglen = msg ? strlen(msg) : 0; committer = git_committer_info(0); maxlen = strlen(committer) + msglen + 100; logrec = xmalloc(maxlen); len = sprintf(logrec, "%s %s %s\n", sha1_to_hex(old_sha1), sha1_to_hex(new_sha1), committer); if (msglen) len += copy_msg(logrec + len - 1, msg) - 1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1; free(logrec); if (close(logfd) != 0 || written != len) return error("Unable to append to %s", log_file); return 0; }
static int is_branch(const char *refname) { return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/"); }
struct transport *transport_get(struct remote *remote, const char *url) { const char *helper; struct transport *ret = xcalloc(1, sizeof(*ret)); ret->progress = isatty(2); if (!remote) die("No remote provided to transport_get()"); ret->got_remote_refs = 0; ret->remote = remote; helper = remote->foreign_vcs; if (!url && remote->url) url = remote->url[0]; ret->url = url; /* maybe it is a foreign URL? */ if (url) { const char *p = url; while (is_urlschemechar(p == url, *p)) p++; if (!prefixcmp(p, "::")) helper = xstrndup(url, p - url); } if (helper) { transport_helper_init(ret, helper); } else if (!prefixcmp(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; ret->smart_options = NULL; } else if (is_local(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->get_refs_list = get_refs_from_bundle; ret->fetch = fetch_refs_from_bundle; ret->disconnect = close_bundle; ret->smart_options = NULL; } else if (!is_url(url) || !prefixcmp(url, "file://") || !prefixcmp(url, "git://") || !prefixcmp(url, "ssh://") || !prefixcmp(url, "git+ssh://") || !prefixcmp(url, "ssh+git://")) { /* These are builtin smart transports. */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; ret->set_option = NULL; ret->get_refs_list = get_refs_via_connect; ret->fetch = fetch_refs_via_pack; ret->push_refs = git_transport_push; ret->connect = connect_git; ret->disconnect = disconnect_git; ret->smart_options = &(data->options); data->conn = NULL; data->got_remote_heads = 0; } else { /* Unknown protocol in URL. Pass to external handler. */ int len = external_specification_len(url); char *handler = xmalloc(len + 1); handler[len] = 0; strncpy(handler, url, len); transport_helper_init(ret, handler); } if (ret->smart_options) { ret->smart_options->thin = 1; ret->smart_options->uploadpack = "git-upload-pack"; if (remote->uploadpack) ret->smart_options->uploadpack = remote->uploadpack; ret->smart_options->receivepack = "git-receive-pack"; if (remote->receivepack) ret->smart_options->receivepack = remote->receivepack; } return ret; }
int cmd_rev_parse(int argc, const char **argv, const char *prefix) { int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; int has_dashdash = 0; int output_prefix = 0; unsigned char sha1[20]; const char *name = NULL; if (argc > 1 && !strcmp("--parseopt", argv[1])) return cmd_parseopt(argc - 1, argv + 1, prefix); if (argc > 1 && !strcmp("--sq-quote", argv[1])) return cmd_sq_quote(argc - 2, argv + 2); if (argc > 1 && !strcmp("-h", argv[1])) usage(builtin_rev_parse_usage); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--")) { has_dashdash = 1; break; } } prefix = setup_git_directory(); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (as_is) { if (show_file(arg, output_prefix) && as_is < 2) verify_filename(prefix, arg, 0); continue; } if (!strcmp(arg,"-n")) { if (++i >= argc) die("-n requires an argument"); if ((filter & DO_FLAGS) && (filter & DO_REVS)) { show(arg); show(argv[i]); } continue; } if (starts_with(arg, "-n")) { if ((filter & DO_FLAGS) && (filter & DO_REVS)) show(arg); continue; } if (*arg == '-') { if (!strcmp(arg, "--")) { as_is = 2; /* Pass on the "--" if we show anything but files.. */ if (filter & (DO_FLAGS | DO_REVS)) show_file(arg, 0); continue; } if (!strcmp(arg, "--default")) { def = argv[i+1]; i++; continue; } if (!strcmp(arg, "--prefix")) { prefix = argv[i+1]; startup_info->prefix = prefix; output_prefix = 1; i++; continue; } if (!strcmp(arg, "--revs-only")) { filter &= ~DO_NOREV; continue; } if (!strcmp(arg, "--no-revs")) { filter &= ~DO_REVS; continue; } if (!strcmp(arg, "--flags")) { filter &= ~DO_NONFLAGS; continue; } if (!strcmp(arg, "--no-flags")) { filter &= ~DO_FLAGS; continue; } if (!strcmp(arg, "--verify")) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; continue; } if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { quiet = 1; continue; } if (!strcmp(arg, "--short") || starts_with(arg, "--short=")) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; abbrev = DEFAULT_ABBREV; if (arg[7] == '=') abbrev = strtoul(arg + 8, NULL, 10); if (abbrev < MINIMUM_ABBREV) abbrev = MINIMUM_ABBREV; else if (40 <= abbrev) abbrev = 40; continue; } if (!strcmp(arg, "--sq")) { output_sq = 1; continue; } if (!strcmp(arg, "--not")) { show_type ^= REVERSED; continue; } if (!strcmp(arg, "--symbolic")) { symbolic = SHOW_SYMBOLIC_ASIS; continue; } if (!strcmp(arg, "--symbolic-full-name")) { symbolic = SHOW_SYMBOLIC_FULL; continue; } if (starts_with(arg, "--abbrev-ref") && (!arg[12] || arg[12] == '=')) { abbrev_ref = 1; abbrev_ref_strict = warn_ambiguous_refs; if (arg[12] == '=') { if (!strcmp(arg + 13, "strict")) abbrev_ref_strict = 1; else if (!strcmp(arg + 13, "loose")) abbrev_ref_strict = 0; else die("unknown mode for %s", arg); } continue; } if (!strcmp(arg, "--all")) { for_each_ref(show_reference, NULL); continue; } if (starts_with(arg, "--disambiguate=")) { for_each_abbrev(arg + 15, show_abbrev, NULL); continue; } if (!strcmp(arg, "--bisect")) { for_each_ref_in("refs/bisect/bad", show_reference, NULL); for_each_ref_in("refs/bisect/good", anti_reference, NULL); continue; } if (starts_with(arg, "--branches=")) { for_each_glob_ref_in(show_reference, arg + 11, "refs/heads/", NULL); clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--branches")) { for_each_branch_ref(show_reference, NULL); clear_ref_exclusion(&ref_excludes); continue; } if (starts_with(arg, "--tags=")) { for_each_glob_ref_in(show_reference, arg + 7, "refs/tags/", NULL); clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--tags")) { for_each_tag_ref(show_reference, NULL); clear_ref_exclusion(&ref_excludes); continue; } if (starts_with(arg, "--glob=")) { for_each_glob_ref(show_reference, arg + 7, NULL); clear_ref_exclusion(&ref_excludes); continue; } if (starts_with(arg, "--remotes=")) { for_each_glob_ref_in(show_reference, arg + 10, "refs/remotes/", NULL); clear_ref_exclusion(&ref_excludes); continue; } if (!strcmp(arg, "--remotes")) { for_each_remote_ref(show_reference, NULL); clear_ref_exclusion(&ref_excludes); continue; } if (!prefixcmp(arg, "--exclude=")) { add_ref_exclusion(&ref_excludes, arg + 10); continue; } if (!strcmp(arg, "--local-env-vars")) { int i; for (i = 0; local_repo_env[i]; i++) printf("%s\n", local_repo_env[i]); continue; } if (!strcmp(arg, "--show-toplevel")) { const char *work_tree = get_git_work_tree(); if (work_tree) puts(work_tree); continue; } if (!strcmp(arg, "--show-prefix")) { if (prefix) puts(prefix); else putchar('\n'); continue; } if (!strcmp(arg, "--show-cdup")) { const char *pfx = prefix; if (!is_inside_work_tree()) { const char *work_tree = get_git_work_tree(); if (work_tree) printf("%s\n", work_tree); continue; } while (pfx) { pfx = strchr(pfx, '/'); if (pfx) { pfx++; printf("../"); } } putchar('\n'); continue; } if (!strcmp(arg, "--git-dir")) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); static char cwd[PATH_MAX]; int len; if (gitdir) { puts(gitdir); continue; } if (!prefix) { puts(".git"); continue; } if (!getcwd(cwd, PATH_MAX)) die_errno("unable to get current working directory"); len = strlen(cwd); printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : ""); continue; } if (!strcmp(arg, "--resolve-git-dir")) { const char *gitdir = resolve_gitdir(argv[i+1]); if (!gitdir) die("not a gitdir '%s'", argv[i+1]); puts(gitdir); continue; } if (!strcmp(arg, "--is-inside-git-dir")) { printf("%s\n", is_inside_git_dir() ? "true" : "false"); continue; } if (!strcmp(arg, "--is-inside-work-tree")) { printf("%s\n", is_inside_work_tree() ? "true" : "false"); continue; } if (!strcmp(arg, "--is-bare-repository")) { printf("%s\n", is_bare_repository() ? "true" : "false"); continue; } if (starts_with(arg, "--since=")) { show_datestring("--max-age=", arg+8); continue; } if (starts_with(arg, "--after=")) { show_datestring("--max-age=", arg+8); continue; } if (starts_with(arg, "--before=")) { show_datestring("--min-age=", arg+9); continue; } if (starts_with(arg, "--until=")) { show_datestring("--min-age=", arg+8); continue; } if (show_flag(arg) && verify) die_no_single_rev(quiet); continue; } /* Not a flag argument */ if (try_difference(arg)) continue; if (try_parent_shorthands(arg)) continue; name = arg; type = NORMAL; if (*arg == '^') { name++; type = REVERSED; } if (!get_sha1(name, sha1)) { if (verify) revs_count++; else show_rev(type, sha1, name); continue; } if (verify) die_no_single_rev(quiet); if (has_dashdash) die("bad revision '%s'", arg); as_is = 1; if (!show_file(arg, output_prefix)) continue; verify_filename(prefix, arg, 1); } if (verify) { if (revs_count == 1) { show_rev(type, sha1, name); return 0; } else if (revs_count == 0 && show_default()) return 0; die_no_single_rev(quiet); } else show_default(); return 0; }
static const char *rsync_url(const char *url) { return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url; }
int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; unsigned char stash[20]; unsigned char head_sha1[20]; struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; int flag, i, ret = 0, head_subsumed; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; struct commit_list *remoteheads, *p; void *branch_to_free; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) head_commit = NULL; else head_commit = lookup_commit_or_die(head_sha1, "HEAD"); git_config(git_merge_config, NULL); if (branch_mergeoptions) parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); if (shortlog_len < 0) shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; if (verbosity < 0 && show_progress == -1) show_progress = 0; if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; if (!file_exists(git_path("MERGE_HEAD"))) die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ ret = cmd_reset(nargc, nargv, prefix); goto done; } if (read_cache_unmerged()) die_resolve_conflict("merge"); if (file_exists(git_path("MERGE_HEAD"))) { /* * There is no unmerged entry, don't advise 'git * add/rm <file>', just 'git commit'. */ if (advice_resolve_conflict) die(_("You have not concluded your merge (MERGE_HEAD exists).\n" "Please, commit your changes before you can merge.")); else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } if (file_exists(git_path("CHERRY_PICK_HEAD"))) { if (advice_resolve_conflict) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you can merge.")); else die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } resolve_undo_clear(); if (verbosity < 0) show_diffstat = 0; if (squash) { if (!allow_fast_forward) die(_("You cannot combine --squash with --no-ff.")); option_commit = 0; } if (!allow_fast_forward && fast_forward_only) die(_("You cannot combine --no-ff with --ff-only.")); if (!abort_current_merge) { if (!argc) { if (default_to_upstream) argc = setup_with_upstream(&argv); else die(_("No commit specified and merge.defaultToUpstream not set.")); } else if (argc == 1 && !strcmp(argv[0], "-")) argv[0] = "@{-1}"; } if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * This could be traditional "merge <msg> HEAD <commit>..." and * the way we can tell it is to see if the second token is HEAD, * but some people might have misused the interface and used a * committish that is the same as HEAD there instead. * Traditional format never would have "-m" so it is an * additional safety measure to check for it. */ if (!have_message && head_commit && is_old_style_invocation(argc, argv, head_commit->object.sha1)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); } else if (!head_commit) { struct commit *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ if (argc != 1) die(_("Can merge only exactly one commit into " "empty head")); if (squash) die(_("Squash commit into empty head not supported yet")); if (!allow_fast_forward) die(_("Non-fast-forward commit does not make sense into " "an empty head")); remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); remote_head = remoteheads->item; if (!remote_head) die(_("%s - not something we can merge"), argv[0]); read_empty(remote_head->object.sha1, 0); update_ref("initial pull", "HEAD", remote_head->object.sha1, NULL, 0, DIE_ON_ERR); goto done; } else { struct strbuf merge_names = STRBUF_INIT; /* We are invoked directly as the first-class UI. */ head_arg = "HEAD"; /* * All the rest are the commits being merged; prepare * the standard merge summary message to be appended * to the given message. */ remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); for (p = remoteheads; p; p = p->next) merge_name(merge_remote_util(p->item)->name, &merge_names); if (!have_message || shortlog_len) { struct fmt_merge_msg_opts opts; memset(&opts, 0, sizeof(opts)); opts.add_title = !have_message; opts.shortlog_len = shortlog_len; opts.credit_people = (0 < option_edit); fmt_merge_msg(&merge_names, &merge_msg, &opts); if (merge_msg.len) strbuf_setlen(&merge_msg, merge_msg.len - 1); } } if (!head_commit || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); if (verify_signatures) { for (p = remoteheads; p; p = p->next) { struct commit *commit = p->item; char hex[41]; struct signature_check signature_check; memset(&signature_check, 0, sizeof(signature_check)); check_commit_signature(commit, &signature_check); strcpy(hex, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV)); switch (signature_check.result) { case 'G': break; case 'U': die(_("Commit %s has an untrusted GPG signature, " "allegedly by %s."), hex, signature_check.signer); case 'B': die(_("Commit %s has a bad GPG signature " "allegedly by %s."), hex, signature_check.signer); default: /* 'N' */ die(_("Commit %s does not have a GPG signature."), hex); } if (verbosity >= 0 && signature_check.result == 'G') printf(_("Commit %s has a good GPG signature by %s\n"), hex, signature_check.signer); free(signature_check.gpg_output); free(signature_check.gpg_status); free(signature_check.signer); free(signature_check.key); } } strbuf_addstr(&buf, "merge"); for (p = remoteheads; p; p = p->next) strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name); setenv("GIT_REFLOG_ACTION", buf.buf, 0); strbuf_reset(&buf); for (p = remoteheads; p; p = p->next) { struct commit *commit = p->item; strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(commit->object.sha1)); setenv(buf.buf, merge_remote_util(commit)->name, 1); strbuf_reset(&buf); if (!fast_forward_only && merge_remote_util(commit) && merge_remote_util(commit)->obj && merge_remote_util(commit)->obj->type == OBJ_TAG) allow_fast_forward = 0; } if (option_edit < 0) option_edit = default_edit_option(); if (!use_strategies) { if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) add_strategies(pull_twohead, DEFAULT_TWOHEAD); else add_strategies(pull_octopus, DEFAULT_OCTOPUS); } for (i = 0; i < use_strategies_nr; i++) { if (use_strategies[i]->attr & NO_FAST_FORWARD) allow_fast_forward = 0; if (use_strategies[i]->attr & NO_TRIVIAL) allow_trivial = 0; } if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) common = get_merge_bases(head_commit, remoteheads->item, 1); else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); common = get_octopus_merge_bases(list); free(list); } update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1, NULL, 0, DIE_ON_ERR); if (remoteheads && !common) ; /* No common ancestors found. We need a real merge. */ else if (!remoteheads || (!remoteheads->next && !common->next && common->item == remoteheads->item)) { /* * If head can reach all the merge then we are up to date. * but first the most common case of merging one remote. */ finish_up_to_date("Already up-to-date."); goto done; } else if (allow_fast_forward && !remoteheads->next && !common->next && !hashcmp(common->item->object.sha1, head_commit->object.sha1)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct commit *commit; char hex[41]; strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV)); if (verbosity >= 0) printf(_("Updating %s..%s\n"), hex, find_unique_abbrev(remoteheads->item->object.sha1, DEFAULT_ABBREV)); strbuf_addstr(&msg, "Fast-forward"); if (have_message) strbuf_addstr(&msg, " (no commit created; -m option ignored)"); commit = remoteheads->item; if (!commit) { ret = 1; goto done; } if (checkout_fast_forward(head_commit->object.sha1, commit->object.sha1, overwrite_ignore)) { ret = 1; goto done; } finish(head_commit, remoteheads, commit->object.sha1, msg.buf); drop_save(); goto done; } else if (!remoteheads->next && common->next) ; /* * We are not doing octopus and not fast-forward. Need * a real merge. */ else if (!remoteheads->next && !common->next && option_commit) { /* * We are not doing octopus, not fast-forward, and have * only one common. */ refresh_cache(REFRESH_QUIET); if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.sha1, head_commit->object.sha1, remoteheads->item->object.sha1)) { ret = merge_trivial(head_commit, remoteheads); goto done; } printf(_("Nope.\n")); } } else { /* * An octopus. If we can reach all the remote we are up * to date. */ int up_to_date = 1; struct commit_list *j; for (j = remoteheads; j; j = j->next) { struct commit_list *common_one; /* * Here we *have* to calculate the individual * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ common_one = get_merge_bases(head_commit, j->item, 1); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; break; } } if (up_to_date) { finish_up_to_date("Already up-to-date. Yeeah!"); goto done; } } if (fast_forward_only) die(_("Not possible to fast-forward, aborting.")); /* We are going to make a new commit. */ git_committer_info(IDENT_STRICT); /* * At this point, we need a real merge. No matter what strategy * we use, it would operate on the index, possibly affecting the * working tree, and when resolved cleanly, have the desired * tree in the index -- this means that the index must be in * sync with the head commit. The strategies are responsible * to ensure this. */ if (use_strategies_nr == 1 || /* * Stash away the local changes so that we can try more than one. */ save_state(stash)) hashcpy(stash, null_sha1); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); restore_state(head_commit->object.sha1, stash); } if (use_strategies_nr != 1) printf(_("Trying merge strategy %s...\n"), use_strategies[i]->name); /* * Remember which strategy left the state in the working * tree. */ wt_strategy = use_strategies[i]->name; ret = try_merge_strategy(use_strategies[i]->name, common, remoteheads, head_commit, head_arg); if (!option_commit && !ret) { merge_was_ok = 1; /* * This is necessary here just to avoid writing * the tree, but later we will *not* exit with * status code 1 because merge_was_ok is set. */ ret = 1; } if (ret) { /* * The backend exits with 1 when conflicts are * left to be resolved, with 2 when it does not * handle the given merge at all. */ if (ret == 1) { int cnt = evaluate_result(); if (best_cnt <= 0 || cnt <= best_cnt) { best_strategy = use_strategies[i]->name; best_cnt = cnt; } } if (merge_was_ok) break; else continue; } /* Automerge succeeded. */ write_tree_trivial(result_tree); automerge_was_ok = 1; break; } /* * If we have a resulting tree, that means the strategy module * auto resolved the merge cleanly. */ if (automerge_was_ok) { ret = finish_automerge(head_commit, head_subsumed, common, remoteheads, result_tree, wt_strategy); goto done; } /* * Pick the result from the best strategy and have the user fix * it up. */ if (!best_strategy) { restore_state(head_commit->object.sha1, stash); if (use_strategies_nr > 1) fprintf(stderr, _("No merge strategy handled the merge.\n")); else fprintf(stderr, _("Merge with strategy %s failed.\n"), use_strategies[0]->name); ret = 2; goto done; } else if (best_strategy == wt_strategy) ; /* We already have its result in the working tree. */ else { printf(_("Rewinding the tree to pristine...\n")); restore_state(head_commit->object.sha1, stash); printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); try_merge_strategy(best_strategy, common, remoteheads, head_commit, head_arg); } if (squash) finish(head_commit, remoteheads, NULL, NULL); else write_merge_state(remoteheads); if (merge_was_ok) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); else ret = suggest_conflicts(option_renormalize); done: free(branch_to_free); return ret; }
static int match_explicit(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs) { struct ref *matched_src, *matched_dst; const char *dst_value = rs->dst; char *dst_guess; if (rs->pattern || rs->matching) return 0; matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: break; case 0: /* The source could be in the get_sha1() format * not a reference name. :refs/other is a * way to delete 'other' ref at the remote end. */ matched_src = try_explicit_object_name(rs->src); if (!matched_src) return error("src refspec %s does not match any.", rs->src); break; default: return error("src refspec %s matches more than one.", rs->src); } if (!dst_value) { unsigned char sha1[20]; int flag; dst_value = resolve_ref(matched_src->name, sha1, 1, &flag); if (!dst_value || ((flag & REF_ISSYMREF) && prefixcmp(dst_value, "refs/heads/"))) die("%s cannot be resolved to branch.", matched_src->name); } switch (count_refspec_match(dst_value, dst, &matched_dst)) { case 1: break; case 0: if (!memcmp(dst_value, "refs/", 5)) matched_dst = make_linked_ref(dst_value, dst_tail); else if((dst_guess = guess_ref(dst_value, matched_src))) matched_dst = make_linked_ref(dst_guess, dst_tail); else error("unable to push to unqualified destination: %s\n" "The destination refspec neither matches an " "existing ref on the remote nor\n" "begins with refs/, and we are unable to " "guess a prefix based on the source ref.", dst_value); break; default: matched_dst = NULL; error("dst refspec %s matches more than one.", dst_value); break; } if (!matched_dst) return -1; if (matched_dst->peer_ref) return error("dst ref %s receives from more than one src.", matched_dst->name); else { matched_dst->peer_ref = matched_src; matched_dst->force = rs->force; } return 0; }
static void add_repo(const char *base, const char *path, repo_config_fn fn) { struct stat st; struct passwd *pwd; char *rel, *p, *slash; int n; size_t size; if (stat(path, &st)) { fprintf(stderr, "Error accessing %s: %s (%d)\n", path, strerror(errno), errno); return; } if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st)) return; if (!stat(fmt("%s/noweb", path), &st)) return; owner = NULL; if (ctx.cfg.enable_gitweb_owner) git_config_from_file(git_owner_config, fmt("%s/config", path), NULL); if (base == path) rel = xstrdup(fmt("%s", path)); else rel = xstrdup(fmt("%s", path + strlen(base) + 1)); if (!strcmp(rel + strlen(rel) - 5, "/.git")) rel[strlen(rel) - 5] = '\0'; repo = cgit_add_repo(rel); if (ctx.cfg.remove_suffix) if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) *p = '\0'; repo->name = repo->url; repo->path = xstrdup(path); while (!owner) { if ((pwd = getpwuid(st.st_uid)) == NULL) { fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", path, strerror(errno), errno); break; } if (pwd->pw_gecos) if ((p = strchr(pwd->pw_gecos, ','))) *p = '\0'; owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); } repo->owner = owner; p = fmt("%s/description", path); if (!stat(p, &st)) readfile(p, &repo->desc, &size); if (!repo->readme) { p = fmt("%s/README.html", path); if (!stat(p, &st)) repo->readme = "README.html"; } if (ctx.cfg.section_from_path) { n = ctx.cfg.section_from_path; if (n > 0) { slash = rel; while (slash && n && (slash = strchr(slash, '/'))) n--; } else { slash = rel + strlen(rel); while (slash && n && (slash = xstrrchr(rel, slash, '/'))) n++; } if (slash && !n) { *slash = '\0'; repo->section = xstrdup(rel); *slash = '/'; if (!prefixcmp(repo->name, repo->section)) { repo->name += strlen(repo->section); if (*repo->name == '/') repo->name++; } } } p = fmt("%s/cgitrc", path); if (!stat(p, &st)) { config_fn = fn; parse_configfile(xstrdup(p), &repo_config); } }
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, j; int use_stdout = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ int subject_prefix = 0; int ignore_if_in_upstream = 0; int thread = 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; 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; /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ for (i = 1, j = 1; i < argc; i++) { if (!strcmp(argv[i], "--stdout")) use_stdout = 1; else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; else if (!strcmp(argv[i], "-N") || !strcmp(argv[i], "--no-numbered")) { numbered = 0; auto_number = 0; } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) numbered_files = 1; else if (!strcmp(argv[i], "--start-number")) { i++; if (i == argc) die("Need a number for --start-number"); start_number = strtol(argv[i], NULL, 10); } else if (!prefixcmp(argv[i], "--cc=")) { ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5); } else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keep-subject")) { keep_subject = 1; rev.total = -1; } else if (!strcmp(argv[i], "--output-directory") || !strcmp(argv[i], "-o")) { i++; if (argc <= i) die("Which directory?"); if (output_directory) die("Two output directories?"); output_directory = argv[i]; } else if (!strcmp(argv[i], "--signoff") || !strcmp(argv[i], "-s")) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogos committer info %s\n", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } else if (!strcmp(argv[i], "--attach")) { rev.mime_boundary = git_version_string; rev.no_inline = 1; } else if (!prefixcmp(argv[i], "--attach=")) { rev.mime_boundary = argv[i] + 9; rev.no_inline = 1; } else if (!strcmp(argv[i], "--inline")) { rev.mime_boundary = git_version_string; rev.no_inline = 0; } else if (!prefixcmp(argv[i], "--inline=")) { rev.mime_boundary = argv[i] + 9; rev.no_inline = 0; } else if (!strcmp(argv[i], "--ignore-if-in-upstream")) ignore_if_in_upstream = 1; else if (!strcmp(argv[i], "--thread")) thread = 1; else if (!prefixcmp(argv[i], "--in-reply-to=")) in_reply_to = argv[i] + 14; else if (!strcmp(argv[i], "--in-reply-to")) { i++; if (i == argc) die("Need a Message-Id for --in-reply-to"); in_reply_to = argv[i]; } else if (!prefixcmp(argv[i], "--subject-prefix=")) { subject_prefix = 1; rev.subject_prefix = argv[i] + 17; } else if (!prefixcmp(argv[i], "--suffix=")) fmt_patch_suffix = argv[i] + 9; else if (!strcmp(argv[i], "--cover-letter")) cover_letter = 1; else if (!strcmp(argv[i], "--no-binary")) no_binary_diff = 1; else argv[j++] = argv[i]; } argc = j; strbuf_init(&buf, 0); 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, 0); if (start_number < 0) start_number = 1; if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) die ("--subject-prefix and -k are mutually exclusive."); if (numbered_files && use_stdout) die ("--numbered-files and --stdout are mutually exclusive."); argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (!output_directory && !use_stdout) output_directory = prefix; if (output_directory) { if (use_stdout) die("standard output, or directory, which one?"); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die("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. */ } 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) rev.ref_message_id = clean_message_id(in_reply_to); 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) { /* * If we've got the ID to be a reply * to, discard the current ID; * otherwise, make everything a reply * to that. */ if (rev.ref_message_id) free(rev.message_id); else rev.ref_message_id = rev.message_id; } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : get_oneline_for_filename(commit, keep_subject), rev.nr, rev.total)) 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 run_post_create(const char *dirname) { /* If doesn't start with "g_settings_dump_location/"... */ if (!dir_is_in_dump_location(dirname)) { /* Then refuse to operate on it (someone is attacking us??) */ error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location); return 400; /* Bad Request */ } if (!dump_dir_accessible_by_uid(dirname, client_uid)) { if (errno == ENOTDIR) { error_msg("Path '%s' isn't problem directory", dirname); return 404; /* Not Found */ } error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dirname, (long)client_uid); return 403; /* Forbidden */ } int child_stdout_fd; int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd); char *dup_of_dir = NULL; struct strbuf *cmd_output = strbuf_new(); bool child_is_post_create = 1; /* else it is a notify child */ read_child_output: //log("Reading from event fd %d", child_stdout_fd); /* Read streamed data and split lines */ for (;;) { char buf[250]; /* usually we get one line, no need to have big buf */ errno = 0; int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1); if (r <= 0) break; buf[r] = '\0'; /* split lines in the current buffer */ char *raw = buf; char *newline; while ((newline = strchr(raw, '\n')) != NULL) { *newline = '\0'; strbuf_append_str(cmd_output, raw); char *msg = cmd_output->buf; /* Hmm, DUP_OF_DIR: ends up in syslog. move log() into 'else'? */ log("%s", msg); if (child_is_post_create && prefixcmp(msg, "DUP_OF_DIR: ") == 0 ) { free(dup_of_dir); dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: ")); } strbuf_clear(cmd_output); /* jump to next line */ raw = newline + 1; } /* beginning of next line. the line continues by next read */ strbuf_append_str(cmd_output, raw); } /* EOF/error */ /* Wait for child to actually exit, collect status */ int status = 0; if (safe_waitpid(child_pid, &status, 0) <= 0) /* should not happen */ perror_msg("waitpid(%d)", child_pid); /* If it was a "notify[-dup]" event, then we're done */ if (!child_is_post_create) goto ret; /* exit 0 means "this is a good, non-dup dir" */ /* exit with 1 + "DUP_OF_DIR: dir" string => dup */ if (status != 0) { if (WIFSIGNALED(status)) { log("'post-create' on '%s' killed by signal %d", dirname, WTERMSIG(status)); goto delete_bad_dir; } /* else: it is WIFEXITED(status) */ if (!dup_of_dir) { log("'post-create' on '%s' exited with %d", dirname, WEXITSTATUS(status)); goto delete_bad_dir; } } const char *work_dir = (dup_of_dir ? dup_of_dir : dirname); /* Load problem_data (from the *first dir* if this one is a dup) */ struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0); if (!dd) /* dd_opendir already emitted error msg */ goto delete_bad_dir; /* Update count */ char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT); unsigned long count = strtoul(count_str, NULL, 10); /* Don't increase crash count if we are working with newly uploaded * directory (remote crash) which already has its crash count set. */ if ((status != 0 && dup_of_dir) || count == 0) { count++; char new_count_str[sizeof(long)*3 + 2]; sprintf(new_count_str, "%lu", count); dd_save_text(dd, FILENAME_COUNT, new_count_str); /* This condition can be simplified to either * (status * != 0 && * dup_of_dir) or (count == 1). But the * chosen form is much more reliable and safe. We must not call * dd_opendir() to locked dd otherwise we go into a deadlock. */ if (strcmp(dd->dd_dirname, dirname) != 0) { /* Update the last occurrence file by the time file of the new problem */ struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY); char *last_ocr = NULL; if (new_dd) { /* TIME must exists in a valid dump directory but we don't want to die * due to broken duplicated dump directory */ last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME, DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT); dd_close(new_dd); } else { /* dd_opendir() already produced a message with good information about failure */ error_msg("Can't read the last occurrence file from the new dump directory."); } if (!last_ocr) { /* the new dump directory may lie in the dump location for some time */ log("Using current time for the last occurrence file which may be incorrect."); time_t t = time(NULL); last_ocr = xasprintf("%lu", (long)t); } dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr); free(last_ocr); } } /* Reset mode/uig/gid to correct values for all files created by event run */ dd_sanitize_mode_and_owner(dd); dd_close(dd); if (!dup_of_dir) log_notice("New problem directory %s, processing", work_dir); else { log_warning("Deleting problem directory %s (dup of %s)", strrchr(dirname, '/') + 1, strrchr(dup_of_dir, '/') + 1); delete_dump_dir(dirname); } /* Run "notify[-dup]" event */ int fd; child_pid = spawn_event_handler_child( work_dir, (dup_of_dir ? "notify-dup" : "notify"), &fd ); //log("Started notify, fd %d -> %d", fd, child_stdout_fd); xmove_fd(fd, child_stdout_fd); child_is_post_create = 0; strbuf_clear(cmd_output); free(dup_of_dir); dup_of_dir = NULL; goto read_child_output; delete_bad_dir: log_warning("Deleting problem directory '%s'", dirname); delete_dump_dir(dirname); ret: strbuf_free(cmd_output); free(dup_of_dir); close(child_stdout_fd); return 0; }
static char * parse_dump_flows( char *cmd, const uint64_t own_datapath_id ) { if ( prefixcmp( cmd, "dump_flows" ) ) { return NULL; } char *opt = strchr ( cmd, ' ' ); assert( opt ); uint64_t datapath_val; uint32_t in_port_val; uint16_t eth_type_val; uint8_t ip_proto_val; uint64_t metadata_val[ 2 ]; uint16_t udp_src_val; uint16_t udp_dst_val; uint8_t table_id_val; uint32_t out_port_val; uint32_t out_group_val; uint64_t cookie_val[ 2 ]; struct option opts[] = { OPT_UINT64( "--datapath_id", "datapath_id:", OPT_NO_MASK, &datapath_val ), OPT_UINT32( "--in_port", "in_port:", OPT_NO_MASK, &in_port_val ), OPT_UINT16( "--eth_type", "eth_type:", OPT_NO_MASK, ð_type_val ), OPT_UINT8( "--ip_proto", "ip_proto:", OPT_NO_MASK, &ip_proto_val ), OPT_UINT64( "--metadata", "metadata:", OPT_HAS_MASK, &metadata_val ), OPT_UINT16( "--udp_src", "udp_src:", OPT_NO_MASK, &udp_src_val ), OPT_UINT16( "--udp_dst", "udp_dst:", OPT_NO_MASK, &udp_dst_val ), OPT_UINT8( "--table_id", "table_id:", OPT_NO_MASK, &table_id_val ), OPT_UINT64( "--cookie", "cookie:", OPT_HAS_MASK, &cookie_val ), OPT_UINT32( "--out_port", "out_port:", OPT_NO_MASK, &out_port_val ), OPT_UINT32( "--out_group", "out_group:", OPT_NO_MASK, &out_group_val ), OPT_END() }; char *saveptr; const char *sep = " "; for ( opt = strtok_r( opt, sep, &saveptr ); opt; opt = strtok_r( NULL, sep, &saveptr ) ) { parse_option( opt, opts ); if ( *( uint64_t * ) opts[ 0 ].value != own_datapath_id ) { return NULL; } } match *match = create_match(); parse_match( match, opts ); flow_stats *stats = NULL; uint32_t nr_stats; OFDPE ret = get_flow_stats( ( uint8_t ) table_id_val, match, cookie_val[ 0 ], cookie_val[ 1 ], out_port_val, out_group_val, &stats, &nr_stats ); if ( ret != OFDPE_SUCCESS ) { error( "Failed to retrieve flow stats from datapath ( ret = %d ).", ret ); } delete_match( match ); char *reply_buf = xmalloc( PATH_MAX + 1 ); reply_buf[ 0 ] = '\0'; size_t used = 0; int left; const char *term = ","; const char *orig = "{ \"dump_flows\": ["; if ( stats != NULL && nr_stats ) { for ( uint32_t i = 0; i < nr_stats; i++ ) { if ( i != 0 ) { orig = ""; } if ( i == nr_stats - 1 ) { term = "]}"; } left = snprintf( reply_buf + used, PATH_MAX - used, "%s {" DUMP_FLOW_FIELDS "} %s", orig, stats->table_id, stats->duration_sec, stats->priority, stats->idle_timeout, stats->hard_timeout, stats->flags, stats->cookie, stats->packet_count, stats->byte_count, term ); used = ( size_t ) left - used; stats++; } } return reply_buf; }
static int update_local_ref(struct ref *ref, const char *remote, char *display) { struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); const char *pretty_ref = prettify_ref(ref); *display = 0; type = sha1_object_info(ref->new_sha1, NULL); if (type < 0) die("object %s not found", sha1_to_hex(ref->new_sha1)); if (!hashcmp(ref->old_sha1, ref->new_sha1)) { if (verbosity > 0) sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH, "[up to date]", REFCOL_WIDTH, remote, pretty_ref); return 0; } if (current_branch && !strcmp(ref->name, current_branch->name) && !(update_head_ok || is_bare_repository()) && !is_null_sha1(ref->old_sha1)) { /* * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)", SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, pretty_ref); return 1; } if (!is_null_sha1(ref->old_sha1) && !prefixcmp(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-', SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote, pretty_ref, r ? " (unable to update local ref)" : ""); return r; } current = lookup_commit_reference_gently(ref->old_sha1, 1); updated = lookup_commit_reference_gently(ref->new_sha1, 1); if (!current || !updated) { const char *msg; const char *what; int r; if (!strncmp(ref->name, "refs/tags/", 10)) { msg = "storing tag"; what = "[new tag]"; } else { msg = "storing head"; what = "[new branch]"; } r = s_update_ref(msg, ref, 0); sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*', SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref, r ? " (unable to update local ref)" : ""); return r; } if (in_merge_bases(current, &updated, 1)) { char quickref[83]; int r; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, ".."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); r = s_update_ref("fast forward", ref, 1); sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ', SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref, r ? " (unable to update local ref)" : ""); return r; } else if (force || ref->force) { char quickref[84]; int r; strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); strcat(quickref, "..."); strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV)); r = s_update_ref("forced-update", ref, 1); sprintf(display, "%c %-*s %-*s -> %s (%s)", r ? '!' : '+', SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref, r ? "unable to update local ref" : "forced update"); return r; } else { sprintf(display, "! %-*s %-*s -> %s (non fast forward)", SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote, pretty_ref); return 1; } }
static int read_merge_config(const char *var, const char *value, void *cb) { struct ll_merge_driver *fn; const char *ep, *name; int namelen; if (!strcmp(var, "merge.default")) { if (value) default_ll_merge = xstrdup(value); return 0; } /* * We are not interested in anything but "merge.<name>.variable"; * especially, we do not want to look at variables such as * "merge.summary", "merge.tool", and "merge.verbosity". */ if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5) return 0; /* * Find existing one as we might be processing merge.<name>.var2 * after seeing merge.<name>.var1. */ name = var + 6; namelen = ep - name; for (fn = ll_user_merge; fn; fn = fn->next) if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) break; if (!fn) { fn = xcalloc(1, sizeof(struct ll_merge_driver)); fn->name = xmemdupz(name, namelen); fn->fn = ll_ext_merge; *ll_user_merge_tail = fn; ll_user_merge_tail = &(fn->next); } ep++; if (!strcmp("name", ep)) { if (!value) return error("%s: lacks value", var); fn->description = xstrdup(value); return 0; } if (!strcmp("driver", ep)) { if (!value) return error("%s: lacks value", var); /* * merge.<name>.driver specifies the command line: * * command-line * * The command-line will be interpolated with the following * tokens and is given to the shell: * * %O - temporary file name for the merge base. * %A - temporary file name for our version. * %B - temporary file name for the other branches' version. * %L - conflict marker length * * The external merge driver should write the results in the * file named by %A, and signal that it has done with zero exit * status. */ fn->cmdline = xstrdup(value); return 0; } if (!strcmp("recursive", ep)) { if (!value) return error("%s: lacks value", var); fn->recursive = xstrdup(value); return 0; } return 0; }
void http_init(struct remote *remote) { char *low_speed_limit; char *low_speed_time; http_is_verbose = 0; git_config(http_options, NULL); curl_global_init(CURL_GLOBAL_ALL); if (remote && remote->http_proxy) curl_http_proxy = xstrdup(remote->http_proxy); pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache"); no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); #ifdef USE_CURL_MULTI { char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); if (http_max_requests != NULL) max_requests = atoi(http_max_requests); } curlm = curl_multi_init(); if (curlm == NULL) { fprintf(stderr, "Error creating curl multi handle.\n"); exit(1); } #endif if (getenv("GIT_SSL_NO_VERIFY")) curl_ssl_verify = 0; set_from_env(&ssl_cert, "GIT_SSL_CERT"); #if LIBCURL_VERSION_NUM >= 0x070903 set_from_env(&ssl_key, "GIT_SSL_KEY"); #endif #if LIBCURL_VERSION_NUM >= 0x070908 set_from_env(&ssl_capath, "GIT_SSL_CAPATH"); #endif set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO"); set_from_env(&user_agent, "GIT_HTTP_USER_AGENT"); low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); if (low_speed_limit != NULL) curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); if (low_speed_time != NULL) curl_low_speed_time = strtol(low_speed_time, NULL, 10); if (curl_ssl_verify == -1) curl_ssl_verify = 1; curl_session_count = 0; #ifdef USE_CURL_MULTI if (max_requests < 1) max_requests = DEFAULT_MAX_REQUESTS; #endif if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; if (remote && remote->url && remote->url[0]) { http_auth_init(remote->url[0]); if (!ssl_cert_password_required && getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && !prefixcmp(remote->url[0], "https://")) ssl_cert_password_required = 1; } #ifndef NO_CURL_EASY_DUPHANDLE curl_default = get_curl_handle(); #endif }
int main(int argc, char **argv) { int listen_port = 0; struct string_list listen_addr = STRING_LIST_INIT_NODUP; int serve_mode = 0, inetd_mode = 0; const char *pid_file = NULL, *user_name = NULL, *group_name = NULL; int detach = 0; struct credentials *cred = NULL; int i; git_extract_argv0_path(argv[0]); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!prefixcmp(arg, "--listen=")) { string_list_append(&listen_addr, xstrdup_tolower(arg + 9)); continue; } if (!prefixcmp(arg, "--port=")) { char *end; unsigned long n; n = strtoul(arg+7, &end, 0); if (arg[7] && !*end) { listen_port = n; continue; } } if (!strcmp(arg, "--serve")) { serve_mode = 1; continue; } if (!strcmp(arg, "--inetd")) { inetd_mode = 1; log_syslog = 1; continue; } if (!strcmp(arg, "--verbose")) { verbose = 1; continue; } if (!strcmp(arg, "--syslog")) { log_syslog = 1; continue; } if (!strcmp(arg, "--export-all")) { export_all_trees = 1; continue; } if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); continue; } if (!prefixcmp(arg, "--init-timeout=")) { init_timeout = atoi(arg+15); continue; } if (!prefixcmp(arg, "--max-connections=")) { max_connections = atoi(arg+18); if (max_connections < 0) max_connections = 0; /* unlimited */ continue; } if (!strcmp(arg, "--strict-paths")) { strict_paths = 1; continue; } if (!prefixcmp(arg, "--base-path=")) { base_path = arg+12; continue; } if (!strcmp(arg, "--base-path-relaxed")) { base_path_relaxed = 1; continue; } if (!prefixcmp(arg, "--interpolated-path=")) { interpolated_path = arg+20; continue; } if (!strcmp(arg, "--reuseaddr")) { reuseaddr = 1; continue; } if (!strcmp(arg, "--user-path")) { user_path = ""; continue; } if (!prefixcmp(arg, "--user-path=")) { user_path = arg + 12; continue; } if (!prefixcmp(arg, "--pid-file=")) { pid_file = arg + 11; continue; } if (!strcmp(arg, "--detach")) { detach = 1; log_syslog = 1; continue; } if (!prefixcmp(arg, "--user="******"--group=")) { group_name = arg + 8; continue; } if (!prefixcmp(arg, "--enable=")) { enable_service(arg + 9, 1); continue; } if (!prefixcmp(arg, "--disable=")) { enable_service(arg + 10, 0); continue; } if (!prefixcmp(arg, "--allow-override=")) { make_service_overridable(arg + 17, 1); continue; } if (!prefixcmp(arg, "--forbid-override=")) { make_service_overridable(arg + 18, 0); continue; } if (!strcmp(arg, "--")) { ok_paths = &argv[i+1]; break; } else if (arg[0] != '-') { ok_paths = &argv[i]; break; } usage(daemon_usage); } if (log_syslog) { openlog("git-daemon", LOG_PID, LOG_DAEMON); set_die_routine(daemon_die); } else /* avoid splitting a message in the middle */ setvbuf(stderr, NULL, _IOFBF, 4096); if (inetd_mode && (detach || group_name || user_name)) die("--detach, --user and --group are incompatible with --inetd"); if (inetd_mode && (listen_port || (listen_addr.nr > 0))) die("--listen= and --port= are incompatible with --inetd"); else if (listen_port == 0) listen_port = DEFAULT_GIT_PORT; if (group_name && !user_name) die("--group supplied without --user"); if (user_name) cred = prepare_credentials(user_name, group_name); if (strict_paths && (!ok_paths || !*ok_paths)) die("option --strict-paths requires a whitelist"); if (base_path && !is_directory(base_path)) die("base-path '%s' does not exist or is not a directory", base_path); if (inetd_mode) { if (!freopen("/dev/null", "w", stderr)) die_errno("failed to redirect stderr to /dev/null"); } if (inetd_mode || serve_mode) return execute(); if (detach) { daemonize(); loginfo("Ready to rumble"); } else sanitize_stdfds(); if (pid_file) store_pid(pid_file); /* prepare argv for serving-processes */ cld_argv = xmalloc(sizeof (char *) * (argc + 2)); for (i = 0; i < argc; ++i) cld_argv[i] = argv[i]; cld_argv[argc] = "--serve"; cld_argv[argc+1] = NULL; return serve(&listen_addr, listen_port, cred); }
static int handle_options(const char ***argv, int *argc, int *envchanged) { const char **orig_argv = *argv; while (*argc > 0) { const char *cmd = (*argv)[0]; if (cmd[0] != '-') break; /* * For legacy reasons, the "version" and "help" * commands can be written with "--" prepended * to make them look like flags. */ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version")) break; /* * Check remaining flags. */ if (!prefixcmp(cmd, "--exec-path")) { cmd += 11; if (*cmd == '=') git_set_argv_exec_path(cmd + 1); else { puts(git_exec_path()); exit(0); } } else if (!strcmp(cmd, "--html-path")) { puts(system_path(GIT_HTML_PATH)); exit(0); } else if (!strcmp(cmd, "--man-path")) { puts(system_path(GIT_MAN_PATH)); exit(0); } else if (!strcmp(cmd, "--info-path")) { puts(system_path(GIT_INFO_PATH)); exit(0); } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { use_pager = 1; } else if (!strcmp(cmd, "--no-pager")) { use_pager = 0; if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { read_replace_refs = 0; setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--git-dir")) { if (*argc < 2) { fprintf(stderr, "No directory given for --git-dir.\n" ); usage(git_usage_string); } setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (!prefixcmp(cmd, "--git-dir=")) { setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--namespace")) { if (*argc < 2) { fprintf(stderr, "No namespace given for --namespace.\n" ); usage(git_usage_string); } setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (!prefixcmp(cmd, "--namespace=")) { setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--work-tree")) { if (*argc < 2) { fprintf(stderr, "No directory given for --work-tree.\n" ); usage(git_usage_string); } setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (!prefixcmp(cmd, "--work-tree=")) { setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--bare")) { static char git_dir[PATH_MAX+1]; is_bare_repository_cfg = 1; setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "-c")) { if (*argc < 2) { fprintf(stderr, "-c expects a configuration string\n" ); usage(git_usage_string); } git_config_push_parameter((*argv)[1]); (*argv)++; (*argc)--; } else if (!strcmp(cmd, "--literal-pathspecs")) { setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-literal-pathspecs")) { setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; } else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(git_usage_string); } (*argv)++; (*argc)--; } return (*argv) - orig_argv; }
void config_cb(const char *name, const char *value) { if (!strcmp(name, "section") || !strcmp(name, "repo.group")) ctx.cfg.section = xstrdup(value); else if (!strcmp(name, "repo.url")) ctx.repo = cgit_add_repo(value); else if (ctx.repo && !strcmp(name, "repo.path")) ctx.repo->path = trim_end(value, '/'); else if (ctx.repo && !prefixcmp(name, "repo.")) repo_config(ctx.repo, name + 5, value); else if (!strcmp(name, "readme")) ctx.cfg.readme = xstrdup(value); else if (!strcmp(name, "root-title")) ctx.cfg.root_title = xstrdup(value); else if (!strcmp(name, "root-desc")) ctx.cfg.root_desc = xstrdup(value); else if (!strcmp(name, "root-readme")) ctx.cfg.root_readme = xstrdup(value); else if (!strcmp(name, "css")) ctx.cfg.css = xstrdup(value); else if (!strcmp(name, "favicon")) ctx.cfg.favicon = xstrdup(value); else if (!strcmp(name, "footer")) ctx.cfg.footer = xstrdup(value); else if (!strcmp(name, "head-include")) ctx.cfg.head_include = xstrdup(value); else if (!strcmp(name, "header")) ctx.cfg.header = xstrdup(value); else if (!strcmp(name, "logo")) ctx.cfg.logo = xstrdup(value); else if (!strcmp(name, "index-header")) ctx.cfg.index_header = xstrdup(value); else if (!strcmp(name, "index-info")) ctx.cfg.index_info = xstrdup(value); else if (!strcmp(name, "logo-link")) ctx.cfg.logo_link = xstrdup(value); else if (!strcmp(name, "module-link")) ctx.cfg.module_link = xstrdup(value); else if (!strcmp(name, "strict-export")) ctx.cfg.strict_export = xstrdup(value); else if (!strcmp(name, "virtual-root")) { ctx.cfg.virtual_root = trim_end(value, '/'); if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) ctx.cfg.virtual_root = ""; } else if (!strcmp(name, "nocache")) ctx.cfg.nocache = atoi(value); else if (!strcmp(name, "noplainemail")) ctx.cfg.noplainemail = atoi(value); else if (!strcmp(name, "noheader")) ctx.cfg.noheader = atoi(value); else if (!strcmp(name, "snapshots")) ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); else if (!strcmp(name, "enable-filter-overrides")) ctx.cfg.enable_filter_overrides = atoi(value); else if (!strcmp(name, "enable-gitweb-owner")) ctx.cfg.enable_gitweb_owner = atoi(value); else if (!strcmp(name, "enable-index-links")) ctx.cfg.enable_index_links = atoi(value); else if (!strcmp(name, "enable-log-filecount")) ctx.cfg.enable_log_filecount = atoi(value); else if (!strcmp(name, "enable-log-linecount")) ctx.cfg.enable_log_linecount = atoi(value); else if (!strcmp(name, "enable-remote-branches")) ctx.cfg.enable_remote_branches = atoi(value); else if (!strcmp(name, "enable-subject-links")) ctx.cfg.enable_subject_links = atoi(value); else if (!strcmp(name, "enable-tree-linenumbers")) ctx.cfg.enable_tree_linenumbers = atoi(value); else if (!strcmp(name, "max-stats")) ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); else if (!strcmp(name, "cache-size")) ctx.cfg.cache_size = atoi(value); else if (!strcmp(name, "cache-root")) ctx.cfg.cache_root = xstrdup(expand_macros(value)); else if (!strcmp(name, "cache-root-ttl")) ctx.cfg.cache_root_ttl = atoi(value); else if (!strcmp(name, "cache-repo-ttl")) ctx.cfg.cache_repo_ttl = atoi(value); else if (!strcmp(name, "cache-scanrc-ttl")) ctx.cfg.cache_scanrc_ttl = atoi(value); else if (!strcmp(name, "cache-static-ttl")) ctx.cfg.cache_static_ttl = atoi(value); else if (!strcmp(name, "cache-dynamic-ttl")) ctx.cfg.cache_dynamic_ttl = atoi(value); else if (!strcmp(name, "about-filter")) ctx.cfg.about_filter = new_filter(value, 0); else if (!strcmp(name, "commit-filter")) ctx.cfg.commit_filter = new_filter(value, 0); else if (!strcmp(name, "embedded")) ctx.cfg.embedded = atoi(value); else if (!strcmp(name, "max-atom-items")) ctx.cfg.max_atom_items = atoi(value); else if (!strcmp(name, "max-message-length")) ctx.cfg.max_msg_len = atoi(value); else if (!strcmp(name, "max-repodesc-length")) ctx.cfg.max_repodesc_len = atoi(value); else if (!strcmp(name, "max-blob-size")) ctx.cfg.max_blob_size = atoi(value); else if (!strcmp(name, "max-repo-count")) ctx.cfg.max_repo_count = atoi(value); else if (!strcmp(name, "max-commit-count")) ctx.cfg.max_commit_count = atoi(value); else if (!strcmp(name, "project-list")) ctx.cfg.project_list = xstrdup(expand_macros(value)); else if (!strcmp(name, "scan-path")) if (!ctx.cfg.nocache && ctx.cfg.cache_size) process_cached_repolist(expand_macros(value)); else if (ctx.cfg.project_list) scan_projects(expand_macros(value), ctx.cfg.project_list, repo_config); else scan_tree(expand_macros(value), repo_config); else if (!strcmp(name, "section-from-path")) ctx.cfg.section_from_path = atoi(value); else if (!strcmp(name, "source-filter")) ctx.cfg.source_filter = new_filter(value, 1); else if (!strcmp(name, "summary-log")) ctx.cfg.summary_log = atoi(value); else if (!strcmp(name, "summary-branches")) ctx.cfg.summary_branches = atoi(value); else if (!strcmp(name, "summary-tags")) ctx.cfg.summary_tags = atoi(value); else if (!strcmp(name, "side-by-side-diffs")) ctx.cfg.ssdiff = atoi(value); else if (!strcmp(name, "agefile")) ctx.cfg.agefile = xstrdup(value); else if (!strcmp(name, "renamelimit")) ctx.cfg.renamelimit = atoi(value); else if (!strcmp(name, "remove-suffix")) ctx.cfg.remove_suffix = atoi(value); else if (!strcmp(name, "robots")) ctx.cfg.robots = xstrdup(value); else if (!strcmp(name, "clone-prefix")) ctx.cfg.clone_prefix = xstrdup(value); else if (!strcmp(name, "local-time")) ctx.cfg.local_time = atoi(value); else if (!prefixcmp(name, "mimetype.")) add_mimetype(name + 9, value); else if (!strcmp(name, "include")) parse_configfile(expand_macros(value), config_cb); }
static void insert_one_record(struct shortlog *log, const char *author, const char *oneline) { const char *dot3 = log->common_repo_prefix; char *buffer, *p; struct string_list_item *item; char namebuf[1024]; char emailbuf[1024]; size_t len; const char *eol; const char *boemail, *eoemail; struct strbuf subject = STRBUF_INIT; boemail = strchr(author, '<'); if (!boemail) return; eoemail = strchr(boemail, '>'); if (!eoemail) return; /* copy author name to namebuf, to support matching on both name and email */ memcpy(namebuf, author, boemail - author); len = boemail - author; while(len > 0 && isspace(namebuf[len-1])) len--; namebuf[len] = 0; /* copy email name to emailbuf, to allow email replacement as well */ memcpy(emailbuf, boemail+1, eoemail - boemail); emailbuf[eoemail - boemail - 1] = 0; if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) { while (author < boemail && isspace(*author)) author++; for (len = 0; len < sizeof(namebuf) - 1 && author + len < boemail; len++) namebuf[len] = author[len]; while (0 < len && isspace(namebuf[len-1])) len--; namebuf[len] = '\0'; } else len = strlen(namebuf); if (log->email) { size_t room = sizeof(namebuf) - len - 1; int maillen = strlen(emailbuf); snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf); } item = string_list_insert(namebuf, &log->list); if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); /* Skip any leading whitespace, including any blank lines. */ while (*oneline && isspace(*oneline)) oneline++; eol = strchr(oneline, '\n'); if (!eol) eol = oneline + strlen(oneline); if (!prefixcmp(oneline, "[PATCH")) { char *eob = strchr(oneline, ']'); if (eob && (!eol || eob < eol)) oneline = eob + 1; } while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); if (dot3) { int dot3len = strlen(dot3); if (dot3len > 5) { while ((p = strstr(buffer, dot3)) != NULL) { int taillen = strlen(p) - dot3len; memcpy(p, "/.../", 5); memmove(p + 5, p + dot3len, taillen + 1); } } } string_list_append(buffer, item->util); }
int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; unsigned char stash[20]; unsigned char head_sha1[20]; struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; int flag, i; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; struct commit_list **remotes = &remoteheads; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ branch = resolve_ref("HEAD", head_sha1, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) head_commit = NULL; else head_commit = lookup_commit_or_die(head_sha1, "HEAD"); git_config(git_merge_config, NULL); if (branch_mergeoptions) parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); if (verbosity < 0 && show_progress == -1) show_progress = 0; if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; if (!file_exists(git_path("MERGE_HEAD"))) die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ return cmd_reset(nargc, nargv, prefix); } if (read_cache_unmerged()) die_resolve_conflict("merge"); if (file_exists(git_path("MERGE_HEAD"))) { /* * There is no unmerged entry, don't advise 'git * add/rm <file>', just 'git commit'. */ if (advice_resolve_conflict) die(_("You have not concluded your merge (MERGE_HEAD exists).\n" "Please, commit your changes before you can merge.")); else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } if (file_exists(git_path("CHERRY_PICK_HEAD"))) { if (advice_resolve_conflict) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you can merge.")); else die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } resolve_undo_clear(); if (verbosity < 0) show_diffstat = 0; if (squash) { if (!allow_fast_forward) die(_("You cannot combine --squash with --no-ff.")); option_commit = 0; } if (!allow_fast_forward && fast_forward_only) die(_("You cannot combine --no-ff with --ff-only.")); if (!abort_current_merge) { if (!argc && default_to_upstream) argc = setup_with_upstream(&argv); else if (argc == 1 && !strcmp(argv[0], "-")) argv[0] = "@{-1}"; } if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * This could be traditional "merge <msg> HEAD <commit>..." and * the way we can tell it is to see if the second token is HEAD, * but some people might have misused the interface and used a * committish that is the same as HEAD there instead. * Traditional format never would have "-m" so it is an * additional safety measure to check for it. */ if (!have_message && head_commit && is_old_style_invocation(argc, argv, head_commit->object.sha1)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; } else if (!head_commit) { struct object *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ if (argc != 1) die(_("Can merge only exactly one commit into " "empty head")); if (squash) die(_("Squash commit into empty head not supported yet")); if (!allow_fast_forward) die(_("Non-fast-forward commit does not make sense into " "an empty head")); remote_head = want_commit(argv[0]); if (!remote_head) die(_("%s - not something we can merge"), argv[0]); read_empty(remote_head->sha1, 0); update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0, DIE_ON_ERR); return 0; } else { struct strbuf merge_names = STRBUF_INIT; /* We are invoked directly as the first-class UI. */ head_arg = "HEAD"; /* * All the rest are the commits being merged; * prepare the standard
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; struct setup_revision_opt s_r_opt; 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; int quiet = 0; char *branch_name = NULL; 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", 0, header_callback }, { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header", 0, to_callback }, { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", 0, 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_STRING(0, "signature", &signature, "signature", "add a signature"), OPT_BOOLEAN(0, "quiet", &quiet, "don't print the patch filenames"), OPT_END() }; extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; 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.items[i].string); 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.items[i].string); 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.items[i].string); 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.")); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); 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 (rev.show_notes) init_display_notes(&rev.notes_opt); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); else setup_pager(); 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. */ unsigned char sha1[20]; const char *ref; rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); if (ref && !prefixcmp(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ } /* * 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 command line. */ rev.show_root_diff = 1; if (cover_letter) { /* * NEEDSWORK:randomly pick one positive commit to show * diffstat; this is often the tip and the command * happens to do the right thing in most cases, but a * complex command like "--cover-letter a b c ^bottom" * picks "c" and shows diffstat between bottom..c * which may not match what the series represents at * all and totally broken. */ 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; } /* There is nothing to show; it is not an error, though. */ if (!head) return 0; if (!branch_name) branch_name = find_branch_name(&rev); } if (ignore_if_in_upstream) { /* Don't say anything if head and upstream are the same. */ if (rev.pending.nr == 2) { struct object_array_entry *o = rev.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } 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; } 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(rev.ref_message_ids, msgid); } 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, branch_name, quiet); 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.ref_message_ids, rev.message_id); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, &rev, quiet)) 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 print_signature(); } if (!use_stdout) fclose(stdout); } free(list); free(branch_name); string_list_clear(&extra_to, 0); string_list_clear(&extra_cc, 0); string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
/* Get the name for the merge commit's message. */ static void merge_name(const char *remote, struct strbuf *msg) { struct object *remote_head; unsigned char branch_head[20], buf_sha[20]; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; char *found_ref; int len, early; strbuf_branchname(&bname, remote); remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); remote_head = want_commit(remote); if (!remote_head) die(_("'%s' does not point to a commit"), remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { if (!prefixcmp(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } if (!prefixcmp(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } } /* See if remote matches <name>^^^.. or <name>~<number> */ for (len = 0, ptr = remote + strlen(remote); remote < ptr && ptr[-1] == '^'; ptr--) len++; if (len) early = 1; else { early = 0; ptr = strrchr(remote, '~'); if (ptr) { int seen_nonzero = 0; len++; /* count ~ */ while (*++ptr && isdigit(*ptr)) { seen_nonzero |= (*ptr != '0'); len++; } if (*ptr) len = 0; /* not ...~<number> */ else if (seen_nonzero) early = 1; else if (len == 1) early = 1; /* "name~" is "name~1"! */ } } if (len) { struct strbuf truname = STRBUF_INIT; strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); if (resolve_ref(truname.buf, buf_sha, 1, NULL)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), truname.buf + 11, (early ? " (early part)" : "")); strbuf_release(&truname); goto cleanup; } } if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { const char *filename; FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; filename = git_path("FETCH_HEAD"); fp = fopen(filename, "r"); if (!fp) die_errno(_("could not open '%s' for reading"), filename); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); if (ptr) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->sha1), remote); cleanup: strbuf_release(&buf); strbuf_release(&bname); }
static int find_common(int fd[2], unsigned char *result_sha1, struct ref *refs) { int fetching; int count = 0, flushes = 0, retval; const unsigned char *sha1; unsigned in_vain = 0; int got_continue = 0; if (marked) for_each_ref(clear_marks, NULL); marked = 1; for_each_ref(rev_list_insert_ref, NULL); fetching = 0; for ( ; refs ; refs = refs->next) { unsigned char *remote = refs->old_sha1; struct object *o; /* * If that object is complete (i.e. it is an ancestor of a * local ref), we tell them we have it but do not have to * tell them about its ancestors, which they already know * about. * * We use lookup_object here because we are only * interested in the case we *know* the object is * reachable and we have already scanned it. */ if (((o = lookup_object(remote)) != NULL) && (o->flags & COMPLETE)) { continue; } if (!fetching) packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n", sha1_to_hex(remote), (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), (args.include_tag ? " include-tag" : ""), (prefer_ofs_delta ? " ofs-delta" : "")); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } if (is_repository_shallow()) write_shallow_commits(fd[1], 1); if (args.depth > 0) packet_write(fd[1], "deepen %d", args.depth); packet_flush(fd[1]); if (!fetching) return 1; if (args.depth > 0) { char line[1024]; unsigned char sha1[20]; while (packet_read_line(fd[0], line, sizeof(line))) { if (!prefixcmp(line, "shallow ")) { if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); register_shallow(sha1); continue; } if (!prefixcmp(line, "unshallow ")) { if (get_sha1_hex(line + 10, sha1)) die("invalid unshallow line: %s", line); if (!lookup_object(sha1)) die("object not found: %s", line); /* make sure that it is parsed as shallow */ if (!parse_object(sha1)) die("error in object: %s", line); if (unregister_shallow(sha1)) die("no shallow found: %s", line); continue; } die("expected shallow/unshallow, got %s", line); } } flushes = 0; retval = -1; while ((sha1 = get_rev())) { packet_write(fd[1], "have %s\n", sha1_to_hex(sha1)); if (args.verbose) fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); in_vain++; if (!(31 & ++count)) { int ack; packet_flush(fd[1]); flushes++; /* * We keep one window "ahead" of the other side, and * will wait for an ACK only on the next one */ if (count == 32) continue; do { ack = get_ack(fd[0], result_sha1); if (args.verbose && ack) fprintf(stderr, "got ack %d %s\n", ack, sha1_to_hex(result_sha1)); if (ack == 1) { flushes = 0; multi_ack = 0; retval = 0; goto done; } else if (ack == 2) { struct commit *commit = lookup_commit(result_sha1); mark_common(commit, 0, 1); retval = 0; in_vain = 0; got_continue = 1; } } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { if (args.verbose) fprintf(stderr, "giving up\n"); break; /* give up */ } } } done: packet_write(fd[1], "done\n"); if (args.verbose) fprintf(stderr, "done\n"); if (retval != 0) { multi_ack = 0; flushes++; } while (flushes || multi_ack) { int ack = get_ack(fd[0], result_sha1); if (ack) { if (args.verbose) fprintf(stderr, "got ack (%d) %s\n", ack, sha1_to_hex(result_sha1)); if (ack == 1) return 0; multi_ack = 1; continue; } flushes--; } /* it is no error to fetch into a completely empty repo */ return count ? retval : 0; }
/* * Note. This is used only by "push"; refspec matching rules for * push and fetch are subtly different, so do not try to reuse it * without thinking. */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, const char **refspec, int flags) { struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; static const char *default_refspec[] = { ":", 0 }; if (!nr_refspec) { nr_refspec = 1; refspec = default_refspec; } rs = parse_push_refspec(nr_refspec, (const char **) refspec); if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) return -1; /* pick the remainder */ for ( ; src; src = src->next) { struct ref *dst_peer; const struct refspec *pat = NULL; char *dst_name; if (src->peer_ref) continue; pat = check_pattern_match(rs, nr_refspec, src); if (!pat) continue; if (pat->matching) { /* * "matching refs"; traditionally we pushed everything * including refs outside refs/heads/ hierarchy, but * that does not make much sense these days. */ if (!send_mirror && prefixcmp(src->name, "refs/heads/")) continue; dst_name = xstrdup(src->name); } else { const char *dst_side = pat->dst ? pat->dst : pat->src; dst_name = xmalloc(strlen(dst_side) + strlen(src->name) - strlen(pat->src) + 2); strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); } dst_peer = find_ref_by_name(dst, dst_name); if (dst_peer) { if (dst_peer->peer_ref) /* We're already sending something to this ref. */ goto free_name; } else { if (pat->matching && !(send_all || send_mirror)) /* * Remote doesn't have it, and we have no * explicit pattern, and we don't have * --all nor --mirror. */ goto free_name; /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = src; dst_peer->force = pat->force; free_name: free(dst_name); } return 0; }