static int cacheinfo_callback(struct parse_opt_ctx_t *ctx, const struct option *opt, int unset) { struct object_id oid; unsigned int mode; const char *path; if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) { if (add_cacheinfo(mode, &oid, path, 0)) die("git update-index: --cacheinfo cannot add %s", path); ctx->argv++; ctx->argc--; return 0; } if (ctx->argc <= 3) return error("option 'cacheinfo' expects <mode>,<sha1>,<path>"); if (strtoul_ui(*++ctx->argv, 8, &mode) || get_oid_hex(*++ctx->argv, &oid) || add_cacheinfo(mode, &oid, *++ctx->argv, 0)) die("git update-index: --cacheinfo cannot add %s", *ctx->argv); ctx->argc -= 3; return 0; }
static int get_reachable_list(struct object_array *src, struct object_array *reachable) { struct child_process cmd = CHILD_PROCESS_INIT; int i; struct object *o; char namebuf[42]; /* ^ + SHA-1 + LF */ if (do_reachable_revlist(&cmd, src, reachable) < 0) return -1; while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) { struct object_id sha1; if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1)) break; o = lookup_object(sha1.hash); if (o && o->type == OBJ_COMMIT) { o->flags &= ~TMP_MARK; } } for (i = get_max_object_index(); 0 < i; i--) { o = get_indexed_object(i - 1); if (o && o->type == OBJ_COMMIT && (o->flags & TMP_MARK)) { add_object_array(o, NULL, reachable); o->flags &= ~TMP_MARK; } } close(cmd.out); if (finish_command(&cmd)) return -1; return 0; }
int cmd_pack_redundant(int argc, const char **argv, const char *prefix) { int i; struct pack_list *min, *red, *pl; struct llist *ignore; struct object_id *oid; char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */ if (argc == 2 && !strcmp(argv[1], "-h")) usage(pack_redundant_usage); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) { i++; break; } if (!strcmp(arg, "--all")) { load_all_packs = 1; continue; } if (!strcmp(arg, "--verbose")) { verbose = 1; continue; } if (!strcmp(arg, "--alt-odb")) { alt_odb = 1; continue; } if (*arg == '-') usage(pack_redundant_usage); else break; } if (load_all_packs) load_all(); else while (*(argv + i) != NULL) add_pack_file(*(argv + i++)); if (local_packs == NULL) die("Zero packs found!"); load_all_objects(); cmp_local_packs(); if (alt_odb) scan_alt_odb_packs(); /* ignore objects given on stdin */ llist_init(&ignore); if (!isatty(0)) { while (fgets(buf, sizeof(buf), stdin)) { oid = xmalloc(sizeof(*oid)); if (get_oid_hex(buf, oid)) die("Bad object ID on stdin: %s", buf); llist_insert_sorted_unique(ignore, oid, NULL); } } llist_sorted_difference_inplace(all_objects, ignore); pl = local_packs; while (pl) { llist_sorted_difference_inplace(pl->unique_objects, ignore); pl = pl->next; } minimize(&min); if (verbose) { fprintf(stderr, "There are %lu packs available in alt-odbs.\n", (unsigned long)pack_list_size(altodb_packs)); fprintf(stderr, "The smallest (bytewise) set of packs is:\n"); pl = min; while (pl) { fprintf(stderr, "\t%s\n", pl->pack->pack_name); pl = pl->next; } fprintf(stderr, "containing %lu duplicate objects " "with a total size of %lukb.\n", (unsigned long)get_pack_redundancy(min), (unsigned long)pack_set_bytecount(min)/1024); fprintf(stderr, "A total of %lu unique objects were considered.\n", (unsigned long)all_objects->size); fprintf(stderr, "Redundant packs (with indexes):\n"); } pl = red = pack_list_difference(local_packs, min); while (pl) { printf("%s\n%s\n", sha1_pack_index_name(pl->pack->sha1), pl->pack->pack_name); pl = pl->next; } if (verbose) fprintf(stderr, "%luMB of redundant packs in total.\n", (unsigned long)pack_set_bytecount(red)/(1024*1024)); return 0; }
int cmd_diff_tree(int argc, const char **argv, const char *prefix) { char line[1000]; struct object *tree1, *tree2; static struct rev_info *opt = &log_tree_opt; struct setup_revision_opt s_r_opt; int read_stdin = 0; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_tree_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(opt, prefix); gitmodules_config(); opt->abbrev = 0; opt->diff = 1; opt->disable_stdin = 1; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.tweak = diff_tree_tweak_rev; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, opt, &s_r_opt); while (--argc > 0) { const char *arg = *++argv; if (!strcmp(arg, "--stdin")) { read_stdin = 1; continue; } usage(diff_tree_usage); } /* * NOTE! We expect "a..b" to expand to "^a b" but it is * perfectly valid for revision range parser to yield "b ^a", * which means the same thing. If we get the latter, i.e. the * second one is marked UNINTERESTING, we recover the original * order the user gave, i.e. "a..b", by swapping the trees. */ switch (opt->pending.nr) { case 0: if (!read_stdin) usage(diff_tree_usage); break; case 1: tree1 = opt->pending.objects[0].item; diff_tree_commit_oid(&tree1->oid); break; case 2: tree1 = opt->pending.objects[0].item; tree2 = opt->pending.objects[1].item; if (tree2->flags & UNINTERESTING) { SWAP(tree2, tree1); } diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt); log_tree_diff_flush(opt); break; } if (read_stdin) { int saved_nrl = 0; int saved_dcctc = 0; if (opt->diffopt.detect_rename) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | DIFF_SETUP_USE_CACHE); while (fgets(line, sizeof(line), stdin)) { struct object_id oid; if (get_oid_hex(line, &oid)) { fputs(line, stdout); fflush(stdout); } else { diff_tree_stdin(line); if (saved_nrl < opt->diffopt.needed_rename_limit) saved_nrl = opt->diffopt.needed_rename_limit; if (opt->diffopt.degraded_cc_to_c) saved_dcctc = 1; } } opt->diffopt.degraded_cc_to_c = saved_dcctc; opt->diffopt.needed_rename_limit = saved_nrl; } return diff_result_code(&opt->diffopt, 0); }
static int get_one_patchid(struct object_id *next_oid, struct object_id *result, struct strbuf *line_buf, int stable) { int patchlen = 0, found_next = 0; int before = -1, after = -1; git_SHA_CTX ctx; git_SHA1_Init(&ctx); oidclr(result); while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { char *line = line_buf->buf; const char *p = line; int len; if (!skip_prefix(line, "diff-tree ", &p) && !skip_prefix(line, "commit ", &p) && !skip_prefix(line, "From ", &p) && starts_with(line, "\\ ") && 12 < strlen(line)) continue; if (!get_oid_hex(p, next_oid)) { found_next = 1; break; } /* Ignore commit comments */ if (!patchlen && !starts_with(line, "diff ")) continue; /* Parsing diff header? */ if (before == -1) { if (starts_with(line, "index ")) continue; else if (starts_with(line, "--- ")) before = after = 1; else if (!isalpha(line[0])) break; } /* Looking for a valid hunk header? */ if (before == 0 && after == 0) { if (starts_with(line, "@@ -")) { /* Parse next hunk, but ignore line numbers. */ scan_hunk_header(line, &before, &after); continue; } /* Split at the end of the patch. */ if (!starts_with(line, "diff ")) break; /* Else we're parsing another header. */ if (stable) flush_one_hunk(result, &ctx); before = after = -1; } /* If we get here, we're inside a hunk. */ if (line[0] == '-' || line[0] == ' ') before--; if (line[0] == '+' || line[0] == ' ') after--; /* Compute the sha without whitespace */ len = remove_space(line); patchlen += len; git_SHA1_Update(&ctx, line, len); } if (!found_next) oidclr(next_oid); flush_one_hunk(result, &ctx); return patchlen; }
static void read_index_info(int nul_term_line) { const int hexsz = the_hash_algo->hexsz; struct strbuf buf = STRBUF_INIT; struct strbuf uq = STRBUF_INIT; strbuf_getline_fn getline_fn; getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; while (getline_fn(&buf, stdin) != EOF) { char *ptr, *tab; char *path_name; struct object_id oid; unsigned int mode; unsigned long ul; int stage; /* This reads lines formatted in one of three formats: * * (1) mode SP sha1 TAB path * The first format is what "git apply --index-info" * reports, and used to reconstruct a partial tree * that is used for phony merge base tree when falling * back on 3-way merge. * * (2) mode SP type SP sha1 TAB path * The second format is to stuff "git ls-tree" output * into the index file. * * (3) mode SP sha1 SP stage TAB path * This format is to put higher order stages into the * index file and matches "git ls-files --stage" output. */ errno = 0; ul = strtoul(buf.buf, &ptr, 8); if (ptr == buf.buf || *ptr != ' ' || errno || (unsigned int) ul != ul) goto bad_line; mode = ul; tab = strchr(ptr, '\t'); if (!tab || tab - ptr < hexsz + 1) goto bad_line; if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { stage = tab[-1] - '0'; ptr = tab + 1; /* point at the head of path */ tab = tab - 2; /* point at tail of sha1 */ } else { stage = 0; ptr = tab + 1; /* point at the head of path */ } if (get_oid_hex(tab - hexsz, &oid) || tab[-(hexsz + 1)] != ' ') goto bad_line; path_name = ptr; if (!nul_term_line && path_name[0] == '"') { strbuf_reset(&uq); if (unquote_c_style(&uq, path_name, NULL)) { die("git update-index: bad quoting of path name"); } path_name = uq.buf; } if (!verify_path(path_name, mode)) { fprintf(stderr, "Ignoring path %s\n", path_name); continue; } if (!mode) { /* mode == 0 means there is no such path -- remove */ if (remove_file_from_cache(path_name)) die("git update-index: unable to remove %s", ptr); } else { /* mode ' ' sha1 '\t' name * ptr[-1] points at tab, * ptr[-41] is at the beginning of sha1 */ ptr[-(hexsz + 2)] = ptr[-1] = 0; if (add_cacheinfo(mode, &oid, path_name, stage)) die("git update-index: unable to update %s", path_name); } continue; bad_line: die("malformed index info %s", buf.buf); } strbuf_release(&buf); strbuf_release(&uq); }
static int find_common(struct fetch_negotiator *negotiator, struct fetch_pack_args *args, int fd[2], struct object_id *result_oid, struct ref *refs) { int fetching; int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval; const struct object_id *oid; unsigned in_vain = 0; int got_continue = 0; int got_ready = 0; struct strbuf req_buf = STRBUF_INIT; size_t state_len = 0; if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); if (!args->no_dependents) { mark_tips(negotiator, args->negotiation_tips); for_each_cached_alternate(negotiator, insert_one_alternate_object); } fetching = 0; for ( ; refs ; refs = refs->next) { struct object_id *remote = &refs->old_oid; const char *remote_hex; 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. * * Do this only if args->no_dependents is false (if it is true, * we cannot trust the object flags). */ if (!args->no_dependents && ((o = lookup_object(the_repository, remote->hash)) != NULL) && (o->flags & COMPLETE)) { continue; } remote_hex = oid_to_hex(remote); if (!fetching) { struct strbuf c = STRBUF_INIT; if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed"); if (multi_ack == 1) strbuf_addstr(&c, " multi_ack"); if (no_done) strbuf_addstr(&c, " no-done"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 1) strbuf_addstr(&c, " side-band"); if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative"); if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack"); if (args->no_progress) strbuf_addstr(&c, " no-progress"); if (args->include_tag) strbuf_addstr(&c, " include-tag"); if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta"); if (deepen_since_ok) strbuf_addstr(&c, " deepen-since"); if (deepen_not_ok) strbuf_addstr(&c, " deepen-not"); if (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); if (args->filter_options.choice) strbuf_addstr(&c, " filter"); packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf); strbuf_release(&c); } else packet_buf_write(&req_buf, "want %s\n", remote_hex); fetching++; } if (!fetching) { strbuf_release(&req_buf); packet_flush(fd[1]); return 1; } if (is_repository_shallow(the_repository)) write_shallow_commits(&req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); if (args->deepen_since) { timestamp_t max_age = approxidate(args->deepen_since); packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age); } if (args->deepen_not) { int i; for (i = 0; i < args->deepen_not->nr; i++) { struct string_list_item *s = args->deepen_not->items + i; packet_buf_write(&req_buf, "deepen-not %s", s->string); } } if (server_supports_filtering && args->filter_options.choice) packet_buf_write(&req_buf, "filter %s", args->filter_options.filter_spec); packet_buf_flush(&req_buf); state_len = req_buf.len; if (args->deepen) { char *line; const char *arg; struct object_id oid; send_request(args, fd[1], &req_buf); while ((line = packet_read_line(fd[0], NULL))) { if (skip_prefix(line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), line); register_shallow(the_repository, &oid); continue; } if (skip_prefix(line, "unshallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid unshallow line: %s"), line); if (!lookup_object(the_repository, oid.hash)) die(_("object not found: %s"), line); /* make sure that it is parsed as shallow */ if (!parse_object(the_repository, &oid)) die(_("error in object: %s"), line); if (unregister_shallow(&oid)) die(_("no shallow found: %s"), line); continue; } die(_("expected shallow/unshallow, got %s"), line); } } else if (!args->stateless_rpc) send_request(args, fd[1], &req_buf); if (!args->stateless_rpc) { /* If we aren't using the stateless-rpc interface * we don't need to retain the headers. */ strbuf_setlen(&req_buf, 0); state_len = 0; } flushes = 0; retval = -1; if (args->no_dependents) goto done; while ((oid = negotiator->next(negotiator))) { packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); print_verbose(args, "have %s", oid_to_hex(oid)); in_vain++; if (flush_at <= ++count) { int ack; packet_buf_flush(&req_buf); send_request(args, fd[1], &req_buf); strbuf_setlen(&req_buf, state_len); flushes++; flush_at = next_flush(args->stateless_rpc, count); /* * We keep one window "ahead" of the other side, and * will wait for an ACK only on the next one */ if (!args->stateless_rpc && count == INITIAL_FLUSH) continue; consume_shallow_list(args, fd[0]); do { ack = get_ack(fd[0], result_oid); if (ack) print_verbose(args, _("got %s %d %s"), "ack", ack, oid_to_hex(result_oid)); switch (ack) { case ACK: flushes = 0; multi_ack = 0; retval = 0; goto done; case ACK_common: case ACK_ready: case ACK_continue: { struct commit *commit = lookup_commit(the_repository, result_oid); int was_common; if (!commit) die(_("invalid commit %s"), oid_to_hex(result_oid)); was_common = negotiator->ack(negotiator, commit); if (args->stateless_rpc && ack == ACK_common && !was_common) { /* We need to replay the have for this object * on the next RPC request so the peer knows * it is in common with us. */ const char *hex = oid_to_hex(result_oid); packet_buf_write(&req_buf, "have %s\n", hex); state_len = req_buf.len; /* * Reset in_vain because an ack * for this commit has not been * seen. */ in_vain = 0; } else if (!args->stateless_rpc || ack != ACK_common) in_vain = 0; retval = 0; got_continue = 1; if (ack == ACK_ready) got_ready = 1; break; } } } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { print_verbose(args, _("giving up")); break; /* give up */ } if (got_ready) break; } } done: if (!got_ready || !no_done) { packet_buf_write(&req_buf, "done\n"); send_request(args, fd[1], &req_buf); } print_verbose(args, _("done")); if (retval != 0) { multi_ack = 0; flushes++; } strbuf_release(&req_buf); if (!got_ready || !no_done) consume_shallow_list(args, fd[0]); while (flushes || multi_ack) { int ack = get_ack(fd[0], result_oid); if (ack) { print_verbose(args, _("got %s (%d) %s"), "ack", ack, oid_to_hex(result_oid)); if (ack == ACK) return 0; multi_ack = 1; continue; } flushes--; } /* it is no error to fetch into a completely empty repo */ return count ? retval : 0; }
int notes_merge_commit(struct notes_merge_options *o, struct notes_tree *partial_tree, struct commit *partial_commit, struct object_id *result_oid) { /* * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all * found notes to 'partial_tree'. Write the updated notes tree to * the DB, and commit the resulting tree object while reusing the * commit message and parents from 'partial_commit'. * Finally store the new commit object OID into 'result_oid'. */ DIR *dir; struct dirent *e; struct strbuf path = STRBUF_INIT; const char *buffer = get_commit_buffer(partial_commit, NULL); const char *msg = strstr(buffer, "\n\n"); int baselen; git_path_buf(&path, NOTES_MERGE_WORKTREE); if (o->verbosity >= 3) printf("Committing notes in notes merge worktree at %s\n", path.buf); if (!msg || msg[2] == '\0') die("partial notes commit has empty message"); msg += 2; dir = opendir(path.buf); if (!dir) die_errno("could not open %s", path.buf); strbuf_addch(&path, '/'); baselen = path.len; while ((e = readdir(dir)) != NULL) { struct stat st; struct object_id obj_oid, blob_oid; if (is_dot_or_dotdot(e->d_name)) continue; if (get_oid_hex(e->d_name, &obj_oid)) { if (o->verbosity >= 3) printf("Skipping non-SHA1 entry '%s%s'\n", path.buf, e->d_name); continue; } strbuf_addstr(&path, e->d_name); /* write file as blob, and add to partial_tree */ if (stat(path.buf, &st)) die_errno("Failed to stat '%s'", path.buf); if (index_path(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT)) die("Failed to write blob object from '%s'", path.buf); if (add_note(partial_tree, &obj_oid, &blob_oid, NULL)) die("Failed to add resolved note '%s' to notes tree", path.buf); if (o->verbosity >= 4) printf("Added resolved note for object %s: %s\n", oid_to_hex(&obj_oid), oid_to_hex(&blob_oid)); strbuf_setlen(&path, baselen); } create_notes_commit(partial_tree, partial_commit->parents, msg, strlen(msg), result_oid->hash); unuse_commit_buffer(partial_commit, buffer); if (o->verbosity >= 4) printf("Finalized notes merge commit: %s\n", oid_to_hex(result_oid)); strbuf_release(&path); closedir(dir); return 0; }
/* * Parses the provided refspec 'refspec' and populates the refspec_item 'item'. * Returns 1 if successful and 0 if the refspec is invalid. */ static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch) { size_t llen; int is_glob; const char *lhs, *rhs; int flags; is_glob = 0; lhs = refspec; if (*lhs == '+') { item->force = 1; lhs++; } rhs = strrchr(lhs, ':'); /* * Before going on, special case ":" (or "+:") as a refspec * for pushing matching refs. */ if (!fetch && rhs == lhs && rhs[1] == '\0') { item->matching = 1; return 1; } if (rhs) { size_t rlen = strlen(++rhs); is_glob = (1 <= rlen && strchr(rhs, '*')); item->dst = xstrndup(rhs, rlen); } else { item->dst = NULL; } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && fetch)) return 0; is_glob = 1; } else if (rhs && is_glob) { return 0; } item->pattern = is_glob; item->src = xstrndup(lhs, llen); flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); if (fetch) { struct object_id unused; /* LHS */ if (!*item->src) ; /* empty is ok; it means "HEAD" */ else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused)) item->exact_sha1 = 1; /* ok */ else if (!check_refname_format(item->src, flags)) ; /* valid looking ref is ok */ else return 0; /* RHS */ if (!item->dst) ; /* missing is ok; it is the same as empty */ else if (!*item->dst) ; /* empty is ok; it means "do not store" */ else if (!check_refname_format(item->dst, flags)) ; /* valid looking ref is ok */ else return 0; } else { /* * LHS * - empty is allowed; it means delete. * - when wildcarded, it must be a valid looking ref. * - otherwise, it must be an extended SHA-1, but * there is no existing way to validate this. */ if (!*item->src) ; /* empty is ok */ else if (is_glob) { if (check_refname_format(item->src, flags)) return 0; } else ; /* anything goes, for now */ /* * RHS * - missing is allowed, but LHS then must be a * valid looking ref. * - empty is not allowed. * - otherwise it must be a valid looking ref. */ if (!item->dst) { if (check_refname_format(item->src, flags)) return 0; } else if (!*item->dst) { return 0; } else { if (check_refname_format(item->dst, flags)) return 0; } } return 1; }
/* * Read a previously-exported (and possibly edited) object back from "filename", * interpreting it as "type", and writing the result to the object database. * The sha1 of the written object is returned via sha1. */ static int import_object(struct object_id *oid, enum object_type type, int raw, const char *filename) { int fd; fd = open(filename, O_RDONLY); if (fd < 0) return error_errno("unable to open %s for reading", filename); if (!raw && type == OBJ_TREE) { const char *argv[] = { "mktree", NULL }; struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf result = STRBUF_INIT; cmd.argv = argv; cmd.git_cmd = 1; cmd.in = fd; cmd.out = -1; if (start_command(&cmd)) { close(fd); return error("unable to spawn mktree"); } if (strbuf_read(&result, cmd.out, 41) < 0) { error_errno("unable to read from mktree"); close(fd); close(cmd.out); return -1; } close(cmd.out); if (finish_command(&cmd)) { strbuf_release(&result); return error("mktree reported failure"); } if (get_oid_hex(result.buf, oid) < 0) { strbuf_release(&result); return error("mktree did not return an object name"); } strbuf_release(&result); } else { struct stat st; int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT; if (fstat(fd, &st) < 0) { error_errno("unable to fstat %s", filename); close(fd); return -1; } if (index_fd(oid, fd, &st, type, NULL, flags) < 0) return error("unable to write object to database"); /* index_fd close()s fd for us */ } /* * No need to close(fd) here; both run-command and index-fd * will have done it for us. */ return 0; }
int cgit_ref_path_exists(const char *path, const char *ref, int file_only) { struct object_id oid; unsigned long size; struct pathspec_item path_items = { .match = xstrdup(path), .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (get_oid(ref, &oid)) goto done; if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT) goto done; read_tree_recursive(lookup_commit_reference(the_repository, &oid)->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); done: free(path_items.match); return walk_tree_ctx.found_path; } int cgit_print_file(char *path, const char *head, int file_only) { struct object_id oid; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (get_oid(head, &oid)) return -1; type = oid_object_info(the_repository, &oid, &size); if (type == OBJ_COMMIT) { commit = lookup_commit_reference(the_repository, &oid); read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.found_path) return -1; type = oid_object_info(the_repository, &oid, &size); } if (type == OBJ_BAD) return -1; buf = read_object_file(&oid, &type, &size); if (!buf) return -1; buf[size] = '\0'; html_raw(buf, size); free(buf); return 0; } void cgit_print_blob(const char *hex, char *path, const char *head, int file_only) { struct object_id oid; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = path ? strlen(path) : 0 }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (hex) { if (get_oid_hex(hex, &oid)) { cgit_print_error_page(400, "Bad request", "Bad hex value: %s", hex); return; } } else { if (get_oid(head, &oid)) { cgit_print_error_page(404, "Not found", "Bad ref: %s", head); return; } } type = oid_object_info(the_repository, &oid, &size); if ((!hex) && type == OBJ_COMMIT && path) { commit = lookup_commit_reference(the_repository, &oid); read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); type = oid_object_info(the_repository, &oid, &size); } if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Bad object name: %s", hex); return; } buf = read_object_file(&oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", hex); return; } buf[size] = '\0'; if (buffer_is_binary(buf, size)) ctx.page.mimetype = "application/octet-stream"; else ctx.page.mimetype = "text/plain"; ctx.page.filename = path; html("X-Content-Type-Options: nosniff\n"); html("Content-Security-Policy: default-src 'none'\n"); cgit_print_http_headers(); html_raw(buf, size); free(buf); }
static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; int depth = 0; int has_non_tip = 0; timestamp_t deepen_since = 0; int deepen_rev_list = 0; shallow_nr = 0; for (;;) { struct object *o; const char *features; struct object_id oid_buf; char *line = packet_read_line(0, NULL); const char *arg; reset_timeout(); if (!line) break; if (skip_prefix(line, "shallow ", &arg)) { struct object_id oid; struct object *object; if (get_oid_hex(arg, &oid)) die("invalid shallow line: %s", line); object = parse_object(&oid); if (!object) continue; if (object->type != OBJ_COMMIT) die("invalid shallow object %s", oid_to_hex(&oid)); if (!(object->flags & CLIENT_SHALLOW)) { object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); } continue; } if (skip_prefix(line, "deepen ", &arg)) { char *end = NULL; depth = strtol(arg, &end, 0); if (!end || *end || depth <= 0) die("Invalid deepen: %s", line); continue; } if (skip_prefix(line, "deepen-since ", &arg)) { char *end = NULL; deepen_since = parse_timestamp(arg, &end, 0); if (!end || *end || !deepen_since || /* revisions.c's max_age -1 is special */ deepen_since == -1) die("Invalid deepen-since: %s", line); deepen_rev_list = 1; continue; } if (skip_prefix(line, "deepen-not ", &arg)) { char *ref = NULL; struct object_id oid; if (expand_ref(arg, strlen(arg), &oid, &ref) != 1) die("git upload-pack: ambiguous deepen-not: %s", line); string_list_append(&deepen_not, ref); free(ref); deepen_rev_list = 1; continue; } if (skip_prefix(line, "filter ", &arg)) { if (!filter_capability_requested) die("git upload-pack: filtering capability not negotiated"); parse_list_objects_filter(&filter_options, arg); continue; } if (!skip_prefix(line, "want ", &arg) || get_oid_hex(arg, &oid_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); features = arg + 40; if (parse_feature_request(features, "deepen-relative")) deepen_relative = 1; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; if (parse_feature_request(features, "no-done")) no_done = 1; if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (parse_feature_request(features, "no-progress")) no_progress = 1; if (parse_feature_request(features, "include-tag")) use_include_tag = 1; if (allow_filter && parse_feature_request(features, "filter")) filter_capability_requested = 1; o = parse_object(&oid_buf); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", oid_to_hex(&oid_buf)); die("git upload-pack: not our ref %s", oid_to_hex(&oid_buf)); } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 || is_our_ref(o))) has_non_tip = 1; add_object_array(o, NULL, &want_obj); } } /* * We have sent all our refs already, and the other end * should have chosen out of them. When we are operating * in the stateless RPC mode, however, their choice may * have been based on the set of older refs advertised * by another process that handled the initial request. */ if (has_non_tip) check_non_tip(); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && !deepen_rev_list && shallows.nr == 0) return; if (depth > 0 && deepen_rev_list) die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); if (depth > 0) deepen(depth, deepen_relative, &shallows); else if (deepen_rev_list) { struct argv_array av = ARGV_ARRAY_INIT; int i; argv_array_push(&av, "rev-list"); if (deepen_since) argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since); if (deepen_not.nr) { argv_array_push(&av, "--not"); for (i = 0; i < deepen_not.nr; i++) { struct string_list_item *s = deepen_not.items + i; argv_array_push(&av, s->string); } argv_array_push(&av, "--not"); } for (i = 0; i < want_obj.nr; i++) { struct object *o = want_obj.objects[i].item; argv_array_push(&av, oid_to_hex(&o->oid)); } deepen_by_rev_list(av.argc, av.argv, &shallows); argv_array_clear(&av); } else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) register_shallow(&shallows.objects[i].item->oid); } shallow_nr += shallows.nr; object_array_clear(&shallows); }