static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_array *list) { unsigned int i; int hit = 0; const unsigned int nr = list->nr; for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) { hit = 1; if (opt->status_only) break; } } return hit; }
static struct commit *get_ref(const char *ref) { unsigned char sha1[20]; struct object *object; if (get_sha1(ref, sha1)) die("Could not resolve ref '%s'", ref); object = deref_tag(parse_object(sha1), ref, strlen(ref)); if (!object) return NULL; if (object->type == OBJ_TREE) return make_virtual_commit((struct tree*)object, better_branch_name(ref)); if (object->type != OBJ_COMMIT) return NULL; if (parse_commit((struct commit *)object)) die("Could not parse commit '%s'", sha1_to_hex(object->sha1)); return (struct commit *)object; }
int git_deref_tag(const unsigned char *tagsha1, GIT_HASH refhash) { struct object *obj = NULL; obj = parse_object(tagsha1); if (!obj) return -1; if (obj->type == OBJ_TAG) { obj = deref_tag(obj, "", 0); if (!obj) return -1; memcpy(refhash, obj->sha1, sizeof(GIT_HASH)); return 0; } return -1; }
static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { FILE *fp = cb_data; struct object *o = parse_object(sha1); if (!o) return -1; if (fprintf(fp, "%s %s\n", sha1_to_hex(sha1), path) < 0) return -1; if (o->type == OBJ_TAG) { o = deref_tag(o, path, 0); if (o) if (fprintf(fp, "%s %s^{}\n", sha1_to_hex(o->sha1), path) < 0) return -1; } return 0; }
static int show_text_ref(const char *name, const unsigned char *sha1, int flag, void *cb_data) { const char *name_nons = strip_namespace(name); struct strbuf *buf = cb_data; struct object *o = parse_object(sha1); if (!o) return 0; strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons); if (o->type == OBJ_TAG) { o = deref_tag(o, name, 0); if (!o) return 0; strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1), name_nons); } return 0; }
static int show_text_ref(const char *name, const struct object_id *oid, int flag, void *cb_data) { const char *name_nons = strip_namespace(name); struct strbuf *buf = cb_data; struct object *o = parse_object(oid->hash); if (!o) return 0; strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons); if (o->type == OBJ_TAG) { o = deref_tag(o, name, 0); if (!o) return 0; strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid), name_nons); } return 0; }
static int add_info_ref(const char *path, const struct object_id *oid, int flag, void *cb_data) { FILE *fp = cb_data; struct object *o = parse_object(oid->hash); if (!o) return -1; if (fprintf(fp, "%s %s\n", oid_to_hex(oid), path) < 0) return -1; if (o->type == OBJ_TAG) { o = deref_tag(o, path, 0); if (o) if (fprintf(fp, "%s %s^{}\n", oid_to_hex(&o->oid), path) < 0) return -1; } return 0; }
static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; struct object *o; struct ref *ref; ref = alloc_ref(ls->dentry_name); if (http_fetch_ref(repo->url, ref) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", ls->dentry_name, repo->url); aborted = 1; free(ref); return; } o = parse_object(&ref->old_oid); if (!o) { fprintf(stderr, "Unable to parse object %s for remote ref %s\n", oid_to_hex(&ref->old_oid), ls->dentry_name); aborted = 1; free(ref); return; } strbuf_addf(buf, "%s\t%s\n", oid_to_hex(&ref->old_oid), ls->dentry_name); if (o->type == OBJ_TAG) { o = deref_tag(o, ls->dentry_name, 0); if (o) strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid), ls->dentry_name); } free(ref); }
static int handle_one_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data) { struct pack_refs_cb_data *cb = cb_data; int is_tag_ref; /* Do not pack the symbolic refs */ if ((flags & REF_ISSYMREF)) return 0; is_tag_ref = !prefixcmp(path, "refs/tags/"); /* ALWAYS pack refs that were already packed or are tags */ if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED)) return 0; fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path); if (is_tag_ref) { struct object *o = parse_object(sha1); if (o->type == OBJ_TAG) { o = deref_tag(o, path, 0); if (o) fprintf(cb->refs_file, "^%s\n", sha1_to_hex(o->sha1)); } } if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) { int namelen = strlen(path) + 1; struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); hashcpy(n->sha1, sha1); strcpy(n->name, path); n->next = cb->ref_to_prune; cb->ref_to_prune = n; } return 0; }
static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_array *list) { unsigned int i; int hit = 0; const unsigned int nr = list->nr; for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); /* load the gitmodules file for this rev */ if (recurse_submodules) { submodule_free(); gitmodules_config_sha1(real_obj->oid.hash); } if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) { hit = 1; if (opt->status_only) break; } } return hit; }
static struct commit *dwim_reverse_initial(struct rev_info *revs, const char **name_p) { /* * DWIM "git blame --reverse ONE -- PATH" as * "git blame --reverse ONE..HEAD -- PATH" but only do so * when it makes sense. */ struct object *obj; struct commit *head_commit; struct object_id head_oid; if (revs->pending.nr != 1) return NULL; /* Is that sole rev a committish? */ obj = revs->pending.objects[0].item; obj = deref_tag(obj, NULL, 0); if (obj->type != OBJ_COMMIT) return NULL; /* Do we have HEAD? */ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) return NULL; head_commit = lookup_commit_reference_gently(&head_oid, 1); if (!head_commit) return NULL; /* Turn "ONE" into "ONE..HEAD" then */ obj->flags |= UNINTERESTING; add_pending_object(revs, &head_commit->object, "HEAD"); if (name_p) *name_p = revs->pending.objects[0].name; return (struct commit *)obj; }
static struct commit *find_single_final(struct rev_info *revs, const char **name_p) { int i; struct commit *found = NULL; const char *name = NULL; for (i = 0; i < revs->pending.nr; i++) { struct object *obj = revs->pending.objects[i].item; if (obj->flags & UNINTERESTING) continue; obj = deref_tag(obj, NULL, 0); if (obj->type != OBJ_COMMIT) die("Non commit %s?", revs->pending.objects[i].name); if (found) die("More than one commit to dig from %s and %s?", revs->pending.objects[i].name, name); found = (struct commit *)obj; name = revs->pending.objects[i].name; } if (name_p) *name_p = xstrdup_or_null(name); return found; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0; int seen_dashdash = 0; int external_grep_allowed__ignored; struct grep_opt opt; struct object_array list = { 0, 0, NULL }; const char **paths = NULL; int i; int dummy; int nongit = 0, use_index = 1; struct option options[] = { OPT_BOOLEAN(0, "cached", &cached, "search in index instead of in the work tree"), OPT_BOOLEAN(0, "index", &use_index, "--no-index finds in contents not managed by git"), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, "show non-matching lines"), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, "case insensitive matching"), OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, "match patterns only at word boundaries"), OPT_SET_INT('a', "text", &opt.binary, "process binary files as text", GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, "don't match patterns in binary files", GREP_BINARY_NOMATCH), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", "descend at most <depth> levels", PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), OPT_BIT('E', "extended-regexp", &opt.regflags, "use extended POSIX regular expressions", REG_EXTENDED), OPT_NEGBIT('G', "basic-regexp", &opt.regflags, "use basic POSIX regular expressions (default)", REG_EXTENDED), OPT_BOOLEAN('F', "fixed-strings", &opt.fixed, "interpret patterns as fixed strings"), OPT_GROUP(""), OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"), OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1), OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1), OPT_NEGBIT(0, "full-name", &opt.relative, "show filenames relative to top directory", 1), OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, "show only filenames instead of matching lines"), OPT_BOOLEAN(0, "name-only", &opt.name_only, "synonym for --files-with-matches"), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, "show only the names of files without match"), OPT_BOOLEAN('z', "null", &opt.null_following_name, "print NUL after filenames"), OPT_BOOLEAN('c', "count", &opt.count, "show the number of matches instead of matching lines"), OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1), OPT_GROUP(""), OPT_CALLBACK('C', NULL, &opt, "n", "show <n> context lines before and after matches", context_callback), OPT_INTEGER('B', NULL, &opt.pre_context, "show <n> context lines before matches"), OPT_INTEGER('A', NULL, &opt.post_context, "show <n> context lines after matches"), OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", context_callback), OPT_BOOLEAN('p', "show-function", &opt.funcname, "show a line with the function name before matches"), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, "file", "read patterns from file", file_callback), { OPTION_CALLBACK, 'e', NULL, &opt, "pattern", "match <pattern>", PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, "combine patterns specified with -e", PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOLEAN(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, open_callback }, { OPTION_CALLBACK, ')', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, close_callback }, OPT_BOOLEAN('q', "quiet", &opt.status_only, "indicate hit with exit status without output"), OPT_BOOLEAN(0, "all-match", &opt.all_match, "show only matches from files that match all patterns"), OPT_GROUP(""), OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, "allow calling of grep(1) (ignored by this build)"), { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage", PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; prefix = setup_git_directory_gently(&nongit); /* * 'git grep -h', unlike 'git grep -h <pattern>', is a request * to show usage information and exit. */ if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); memset(&opt, 0, sizeof(opt)); opt.prefix = prefix; opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.relative = 1; opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; opt.max_depth = -1; strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); opt.color = -1; git_config(grep_config, &opt); if (opt.color == -1) opt.color = git_use_color_default; /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or * -f, we take the first unrecognized non option to be the * pattern, but then what follows it must be zero or more * valid refs up to the -- (if exists), and then existing * paths. If there is an explicit pattern, then the first * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ argc = parse_options(argc, argv, prefix, options, grep_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_STOP_AT_NON_OPTION | PARSE_OPT_NO_INTERNAL_HELP); if (use_index && nongit) /* die the same way as if we did it at the beginning */ setup_git_directory(); /* * skip a -- separator; we know it cannot be * separating revisions from pathnames if * we haven't even had any patterns yet */ if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) { argv++; argc--; } /* First unrecognized non-option token */ if (argc > 0 && !opt.pattern_list) { append_grep_pattern(&opt, argv[0], "command line", 0, GREP_PATTERN); argv++; argc--; } if (!opt.pattern_list) die("no pattern given."); if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; if ((opt.regflags != REG_NEWLINE) && opt.fixed) die("cannot mix --fixed-strings and regexp"); #ifndef NO_PTHREADS if (online_cpus() == 1 || !grep_threads_ok(&opt)) use_threads = 0; if (use_threads) start_threads(&opt); #else use_threads = 0; #endif compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object %s", arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j]); } if (i < argc) paths = get_pathspec(prefix, argv + i); else if (prefix) { paths = xcalloc(2, sizeof(const char *)); paths[0] = prefix; paths[1] = NULL; } if (!use_index) { int hit; if (cached) die("--cached cannot be used with --no-index."); if (list.nr) die("--no-index cannot be used with revs."); hit = grep_directory(&opt, paths); if (use_threads) hit |= wait_all(); return !hit; } if (!list.nr) { int hit; if (!cached) setup_work_tree(); hit = grep_cache(&opt, paths, cached); if (use_threads) hit |= wait_all(); return !hit; } if (cached) die("both --cached and trees are given."); for (i = 0; i < list.nr; i++) { struct object *real_obj; real_obj = deref_tag(list.objects[i].item, NULL, 0); if (grep_object(&opt, paths, real_obj, list.objects[i].name)) { hit = 1; if (opt.status_only) break; } } if (use_threads) hit |= wait_all(); free_grep_patterns(&opt); return !hit; }
static void shortlog(const char *name, struct origin_data *origin_data, struct commit *head, struct rev_info *rev, int limit, struct strbuf *out) { int i, count = 0; struct commit *commit; struct object *branch; struct string_list subjects = STRING_LIST_INIT_DUP; struct string_list authors = STRING_LIST_INIT_DUP; struct string_list committers = STRING_LIST_INIT_DUP; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; struct strbuf sb = STRBUF_INIT; const unsigned char *sha1 = origin_data->sha1; branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); if (!branch || branch->type != OBJ_COMMIT) return; setup_revisions(0, NULL, rev, NULL); add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; if (prepare_revision_walk(rev)) die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { struct pretty_print_context ctx = {0}; if (commit->parents && commit->parents->next) { /* do not list a merge but count committer */ record_person('c', &committers, commit); continue; } if (!count) /* the 'tip' committer */ record_person('c', &committers, commit); record_person('a', &authors, commit); count++; if (subjects.nr > limit) continue; format_commit_message(commit, "%s", &sb, &ctx); strbuf_ltrim(&sb); if (!sb.len) string_list_append(&subjects, sha1_to_hex(commit->object.sha1)); else string_list_append(&subjects, strbuf_detach(&sb, NULL)); } add_people_info(out, &authors, &committers); if (count > limit) strbuf_addf(out, "\n* %s: (%d commits)\n", name, count); else strbuf_addf(out, "\n* %s:\n", name); if (origin_data->is_local_branch && use_branch_desc) add_branch_desc(out, name); for (i = 0; i < subjects.nr; i++) if (i >= limit) strbuf_addf(out, " ...\n"); else strbuf_addf(out, " %s\n", subjects.items[i].string); clear_commit_marks((struct commit *)branch, flags); clear_commit_marks(head, flags); free_commit_list(rev->commits); rev->commits = NULL; rev->pending.nr = 0; string_list_clear(&authors, 0); string_list_clear(&committers, 0); string_list_clear(&subjects, 0); }
static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata) { struct object *obj; const char *hex; unsigned char peeled[20]; if (tags_only || heads_only) { int match; match = heads_only && !prefixcmp(refname, "refs/heads/"); match |= tags_only && !prefixcmp(refname, "refs/tags/"); if (!match) return 0; } if (pattern) { int reflen = strlen(refname); const char **p = pattern, *m; while ((m = *p++) != NULL) { int len = strlen(m); if (len > reflen) continue; if (memcmp(m, refname + reflen - len, len)) continue; if (len == reflen) goto match; /* "--verify" requires an exact match */ if (verify) continue; if (refname[reflen - len - 1] == '/') goto match; } return 0; } match: found_match++; /* This changes the semantics slightly that even under quiet we * detect and return error if the repository is corrupt and * ref points at a nonexistent object. */ if (!has_sha1_file(sha1)) die("git show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1)); if (quiet) return 0; show_one(refname, sha1); if (!deref_tags) return 0; if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) { if (!is_null_sha1(peeled)) { hex = find_unique_abbrev(peeled, abbrev); printf("%s %s^{}\n", hex, refname); } } else { obj = parse_object(sha1); if (!obj) die("git show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1)); if (obj->type == OBJ_TAG) { obj = deref_tag(obj, refname, 0); if (!obj) die("git show-ref: bad tag at ref %s (%s)", refname, sha1_to_hex(sha1)); hex = find_unique_abbrev(obj->sha1, abbrev); printf("%s %s^{}\n", hex, refname); } } return 0; }
static void shortlog(const char *name, unsigned char *sha1, struct commit *head, struct rev_info *rev, int limit) { int i, count = 0; struct commit *commit; struct object *branch; struct list subjects = { NULL, NULL, 0, 0 }; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40); if (!branch || branch->type != OBJ_COMMIT) return; setup_revisions(0, NULL, rev, NULL); rev->ignore_merges = 1; add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; if (prepare_revision_walk(rev)) die("revision walk setup failed"); while ((commit = get_revision(rev)) != NULL) { char *oneline, *bol, *eol; /* ignore merges */ if (commit->parents && commit->parents->next) continue; count++; if (subjects.nr > limit) continue; bol = strstr(commit->buffer, "\n\n"); if (bol) { unsigned char c; do { c = *++bol; } while (isspace(c)); if (!c) bol = NULL; } if (!bol) { append_to_list(&subjects, xstrdup(sha1_to_hex( commit->object.sha1)), NULL); continue; } eol = strchr(bol, '\n'); if (eol) { oneline = xmemdupz(bol, eol - bol); } else { oneline = xstrdup(bol); } append_to_list(&subjects, oneline, NULL); } if (count > limit) printf("\n* %s: (%d commits)\n", name, count); else printf("\n* %s:\n", name); for (i = 0; i < subjects.nr; i++) if (i >= limit) printf(" ...\n"); else printf(" %s\n", subjects.list[i]); clear_commit_marks((struct commit *)branch, flags); clear_commit_marks(head, flags); free_commit_list(rev->commits); rev->commits = NULL; rev->pending.nr = 0; free_list(&subjects); }
static int peel_onion(const char *name, int len, unsigned char *sha1) { unsigned char outer[20]; const char *sp; const char *type_string = NULL; struct object *o; /* * "ref^{type}" dereferences ref repeatedly until you cannot * dereference anymore, or you get an object of given type, * whichever comes first. "ref^{}" means just dereference * tags until you get a non-tag. "ref^0" is a shorthand for * "ref^{commit}". "commit^{tree}" could be used to find the * top-level tree of the given commit. */ if (len < 4 || name[len-1] != '}') return -1; for (sp = name + len - 1; name <= sp; sp--) { int ch = *sp; if (ch == '{' && name < sp && sp[-1] == '^') break; } if (sp <= name) return -1; sp++; /* beginning of type name, or closing brace for empty */ if (!strncmp(commit_type, sp, 6) && sp[6] == '}') type_string = commit_type; else if (!strncmp(tree_type, sp, 4) && sp[4] == '}') type_string = tree_type; else if (!strncmp(blob_type, sp, 4) && sp[4] == '}') type_string = blob_type; else if (sp[0] == '}') type_string = NULL; else return -1; if (get_sha1_1(name, sp - name - 2, outer)) return -1; o = parse_object(outer); if (!o) return -1; if (!type_string) { o = deref_tag(o, name, sp - name - 2); if (!o || (!o->parsed && !parse_object(o->sha1))) return -1; memcpy(sha1, o->sha1, 20); } else { /* At this point, the syntax look correct, so * if we do not get the needed object, we should * barf. */ while (1) { if (!o || (!o->parsed && !parse_object(o->sha1))) return -1; if (o->type == type_string) { memcpy(sha1, o->sha1, 20); return 0; } if (o->type == tag_type) o = ((struct tag*) o)->tagged; else if (o->type == commit_type) o = &(((struct commit *) o)->tree->object); else return error("%.*s: expected %s type, but the object dereferences to %s type", len, name, type_string, o->type); if (!o->parsed) parse_object(o->sha1); } } return 0; }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; struct blobinfo blob[2]; int nongit = 0, no_index = 0; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ /* Were we asked to do --no-index explicitly? */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--")) { i++; break; } if (!strcmp(argv[i], "--no-index")) no_index = DIFF_NO_INDEX_EXPLICIT; if (argv[i][0] != '-') break; } if (!no_index) prefix = setup_git_directory_gently(&nongit); /* * Treat git diff with at least one path outside of the * repo the same as if the command would have been executed * outside of a git repository. In this case it behaves * the same way as "git diff --no-index <a> <b>", which acts * as a colourful "diff" replacement. */ if (nongit || ((argc == i + 2) && (!path_inside_repo(prefix, argv[i]) || !path_inside_repo(prefix, argv[i + 1])))) no_index = DIFF_NO_INDEX_IMPLICIT; if (!no_index) gitmodules_config(); init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); precompose_argv(argc, argv); init_revisions(&rev, prefix); if (no_index && argc != i + 2) { if (no_index == DIFF_NO_INDEX_IMPLICIT) { /* * There was no --no-index and there were not two * paths. It is possible that the user intended * to do an inside-repository operation. */ fprintf(stderr, "Not a git repository\n"); fprintf(stderr, "To compare two paths outside a working tree:\n"); } /* Give the usage message for non-repository usage and exit. */ usagef("git diff %s <path> <path>", no_index == DIFF_NO_INDEX_EXPLICIT ? "--no-index" : "[--no-index]"); } if (no_index) /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Scale to real terminal size and respect statGraphWidth config */ rev.diffopt.stat_width = -1; rev.diffopt.stat_graph_width = -1; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); setup_diff_pager(&rev.diffopt); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree(EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *entry = &rev.pending.objects[i]; struct object *obj = entry->item; const char *name = entry->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->oid.hash); obj = deref_tag(obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { obj->flags |= flags; add_object_array(obj, name, &ent); } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); hashcpy(blob[blobs].sha1, obj->oid.hash); blob[blobs].name = name; blob[blobs].mode = entry->mode; blobs++; } else { die(_("unhandled object '%s' given."), name); } } if (rev.prune_data.nr) paths += rev.prune_data.nr; /* * Now, do the arguments look reasonable? */ if (!ent.nr) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) result = builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[1]); else if (ent.objects[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent.objects[0] == * merge-base, ent.objects[ents-2] == A, and * ent.objects[ents-1] == B. Show diff between the * base and B. Note that we pick one merge base at * random if there are more than one. */ result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[ent.nr-1]); } else result = builtin_diff_combined(&rev, argc, argv, ent.objects, ent.nr); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
/* * Mark recent commits available locally and reachable from a local ref as * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as * COMMON_REF (otherwise, we are not planning to participate in negotiation, and * thus do not need COMMON_REF marks). * * The cutoff time for recency is determined by this heuristic: it is the * earliest commit time of the objects in refs that are commits and that we know * the commit time of. */ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator, struct fetch_pack_args *args, struct ref **refs) { struct ref *ref; int old_save_commit_buffer = save_commit_buffer; timestamp_t cutoff = 0; struct oidset loose_oid_set = OIDSET_INIT; int use_oidset = 0; struct loose_object_iter iter = {&loose_oid_set, *refs}; /* Enumerate all loose objects or know refs are not so many. */ use_oidset = !for_each_loose_object(add_loose_objects_to_set, &iter, 0); save_commit_buffer = 0; for (ref = *refs; ref; ref = ref->next) { struct object *o; unsigned int flags = OBJECT_INFO_QUICK; if (use_oidset && !oidset_contains(&loose_oid_set, &ref->old_oid)) { /* * I know this does not exist in the loose form, * so check if it exists in a non-loose form. */ flags |= OBJECT_INFO_IGNORE_LOOSE; } if (!has_object_file_with_flags(&ref->old_oid, flags)) continue; o = parse_object(the_repository, &ref->old_oid); if (!o) continue; /* We already have it -- which may mean that we were * in sync with the other side at some time after * that (it is OK if we guess wrong here). */ if (o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; if (!cutoff || cutoff < commit->date) cutoff = commit->date; } } oidset_clear(&loose_oid_set); if (!args->deepen) { for_each_ref(mark_complete_oid, NULL); for_each_cached_alternate(NULL, mark_alternate_complete); commit_list_sort_by_date(&complete); if (cutoff) mark_recent_complete_commits(args, cutoff); } /* * Mark all complete remote refs as common refs. * Don't mark them common yet; the server has to be told so first. */ for (ref = *refs; ref; ref = ref->next) { struct object *o = deref_tag(the_repository, lookup_object(the_repository, ref->old_oid.hash), NULL, 0); if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE)) continue; negotiator->known_common(negotiator, (struct commit *)o); } save_commit_buffer = old_save_commit_buffer; }
int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), OPT_STRING(0, "refs", &data.ref_filter, "pattern", "only use refs matching <pattern>"), OPT_GROUP(""), OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"), OPT_BOOLEAN(0, "always", &always, "show abbreviated commit object as fallback"), OPT_END(), }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); if (!!all + !!transform_stdin + !!argc > 1) { error("Specify either a list, or --all, not both!"); usage_with_options(name_rev_usage, opts); } if (all || transform_stdin) cutoff = 0; for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *o; struct commit *commit; if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); continue; } o = deref_tag(parse_object(sha1), *argv, 0); if (!o || o->type != OBJ_COMMIT) { fprintf(stderr, "Could not get commit for %s. Skipping.\n", *argv); continue; } commit = (struct commit *)o; if (cutoff > commit->date) cutoff = commit->date; add_object_array((struct object *)commit, *argv, &revs); } if (cutoff) cutoff = cutoff - CUTOFF_DATE_SLOP; for_each_ref(name_ref, &data); if (transform_stdin) { char buffer[2048]; while (!feof(stdin)) { char *p = fgets(buffer, sizeof(buffer), stdin); if (!p) break; name_rev_line(p, &data); } } else if (all) { int i, max; max = get_max_object_index(); for (i = 0; i < max; i++) { struct object *obj = get_indexed_object(i); if (!obj) continue; show_name(obj, NULL, always, allow_undefined, data.name_only); } } else { int i; for (i = 0; i < revs.nr; i++) show_name(revs.objects[i].item, revs.objects[i].name, always, allow_undefined, data.name_only); } return 0; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0; int seen_dashdash = 0; struct grep_opt opt; struct object_array list = { 0, 0, NULL }; const char **paths = NULL; int i; memset(&opt, 0, sizeof(opt)); opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.relative = 1; opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or * -f, we take the first unrecognized non option to be the * pattern, but then what follows it must be zero or more * valid refs up to the -- (if exists), and then existing * paths. If there is an explicit pattern, then the first * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ while (1 < argc) { const char *arg = argv[1]; argc--; argv++; if (!strcmp("--cached", arg)) { cached = 1; continue; } if (!strcmp("--no-ext-grep", arg)) { builtin_grep = 1; continue; } if (!strcmp("-a", arg) || !strcmp("--text", arg)) { opt.binary = GREP_BINARY_TEXT; continue; } if (!strcmp("-i", arg) || !strcmp("--ignore-case", arg)) { opt.regflags |= REG_ICASE; continue; } if (!strcmp("-I", arg)) { opt.binary = GREP_BINARY_NOMATCH; continue; } if (!strcmp("-v", arg) || !strcmp("--invert-match", arg)) { opt.invert = 1; continue; } if (!strcmp("-E", arg) || !strcmp("--extended-regexp", arg)) { opt.regflags |= REG_EXTENDED; continue; } if (!strcmp("-F", arg) || !strcmp("--fixed-strings", arg)) { opt.fixed = 1; continue; } if (!strcmp("-G", arg) || !strcmp("--basic-regexp", arg)) { opt.regflags &= ~REG_EXTENDED; continue; } if (!strcmp("-n", arg)) { opt.linenum = 1; continue; } if (!strcmp("-h", arg)) { opt.pathname = 0; continue; } if (!strcmp("-H", arg)) { opt.pathname = 1; continue; } if (!strcmp("-l", arg) || !strcmp("--name-only", arg) || !strcmp("--files-with-matches", arg)) { opt.name_only = 1; continue; } if (!strcmp("-L", arg) || !strcmp("--files-without-match", arg)) { opt.unmatch_name_only = 1; continue; } if (!strcmp("-z", arg) || !strcmp("--null", arg)) { opt.null_following_name = 1; continue; } if (!strcmp("-c", arg) || !strcmp("--count", arg)) { opt.count = 1; continue; } if (!strcmp("-w", arg) || !strcmp("--word-regexp", arg)) { opt.word_regexp = 1; continue; } if (!prefixcmp(arg, "-A") || !prefixcmp(arg, "-B") || !prefixcmp(arg, "-C") || (arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) { unsigned num; const char *scan; switch (arg[1]) { case 'A': case 'B': case 'C': if (!arg[2]) { if (argc <= 1) die(emsg_missing_context_len); scan = *++argv; argc--; } else scan = arg + 2; break; default: scan = arg + 1; break; } if (strtoul_ui(scan, 10, &num)) die(emsg_invalid_context_len, scan); switch (arg[1]) { case 'A': opt.post_context = num; break; default: case 'C': opt.post_context = num; case 'B': opt.pre_context = num; break; } continue; } if (!strcmp("-f", arg)) { FILE *patterns; int lno = 0; char buf[1024]; if (argc <= 1) die(emsg_missing_argument, arg); patterns = fopen(argv[1], "r"); if (!patterns) die("'%s': %s", argv[1], strerror(errno)); while (fgets(buf, sizeof(buf), patterns)) { int len = strlen(buf); if (len && buf[len-1] == '\n') buf[len-1] = 0; /* ignore empty line like grep does */ if (!buf[0]) continue; append_grep_pattern(&opt, xstrdup(buf), argv[1], ++lno, GREP_PATTERN); } fclose(patterns); argv++; argc--; continue; } if (!strcmp("--not", arg)) { append_grep_pattern(&opt, arg, "command line", 0, GREP_NOT); continue; } if (!strcmp("--and", arg)) { append_grep_pattern(&opt, arg, "command line", 0, GREP_AND); continue; } if (!strcmp("--or", arg)) continue; /* no-op */ if (!strcmp("(", arg)) { append_grep_pattern(&opt, arg, "command line", 0, GREP_OPEN_PAREN); continue; } if (!strcmp(")", arg)) { append_grep_pattern(&opt, arg, "command line", 0, GREP_CLOSE_PAREN); continue; } if (!strcmp("--all-match", arg)) { opt.all_match = 1; continue; } if (!strcmp("-e", arg)) { if (1 < argc) { append_grep_pattern(&opt, argv[1], "-e option", 0, GREP_PATTERN); argv++; argc--; continue; } die(emsg_missing_argument, arg); } if (!strcmp("--full-name", arg)) { opt.relative = 0; continue; } if (!strcmp("--", arg)) { /* later processing wants to have this at argv[1] */ argv--; argc++; break; } if (*arg == '-') usage(builtin_grep_usage); /* First unrecognized non-option token */ if (!opt.pattern_list) { append_grep_pattern(&opt, arg, "command line", 0, GREP_PATTERN); break; } else { /* We are looking at the first path or rev; * it is found at argv[1] after leaving the * loop. */ argc++; argv--; break; } } if (!opt.pattern_list) die("no pattern given."); if ((opt.regflags != REG_NEWLINE) && opt.fixed) die("cannot mix --fixed-strings and regexp"); compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object %s", arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j]); } if (i < argc) { paths = get_pathspec(prefix, argv + i); if (opt.prefix_length && opt.relative) { /* Make sure we do not get outside of paths */ for (i = 0; paths[i]; i++) if (strncmp(prefix, paths[i], opt.prefix_length)) die("git grep: cannot generate relative filenames containing '..'"); } } else if (prefix) { paths = xcalloc(2, sizeof(const char *)); paths[0] = prefix; paths[1] = NULL; } if (!list.nr) { if (!cached) setup_work_tree(); return !grep_cache(&opt, paths, cached); } if (cached) die("both --cached and trees are given."); for (i = 0; i < list.nr; i++) { struct object *real_obj; real_obj = deref_tag(list.objects[i].item, NULL, 0); if (grep_object(&opt, paths, real_obj, list.objects[i].name)) hit = 1; } free_grep_patterns(&opt); return !hit; }
static int everything_local(struct fetch_pack_args *args, struct ref **refs, struct ref **sought, int nr_sought) { struct ref *ref; int retval; int old_save_commit_buffer = save_commit_buffer; timestamp_t cutoff = 0; struct oidset loose_oid_set = OIDSET_INIT; int use_oidset = 0; struct loose_object_iter iter = {&loose_oid_set, *refs}; /* Enumerate all loose objects or know refs are not so many. */ use_oidset = !for_each_loose_object(add_loose_objects_to_set, &iter, 0); save_commit_buffer = 0; for (ref = *refs; ref; ref = ref->next) { struct object *o; unsigned int flags = OBJECT_INFO_QUICK; if (use_oidset && !oidset_contains(&loose_oid_set, &ref->old_oid)) { /* * I know this does not exist in the loose form, * so check if it exists in a non-loose form. */ flags |= OBJECT_INFO_IGNORE_LOOSE; } if (!has_object_file_with_flags(&ref->old_oid, flags)) continue; o = parse_object(&ref->old_oid); if (!o) continue; /* We already have it -- which may mean that we were * in sync with the other side at some time after * that (it is OK if we guess wrong here). */ if (o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; if (!cutoff || cutoff < commit->date) cutoff = commit->date; } } oidset_clear(&loose_oid_set); if (!args->no_dependents) { if (!args->deepen) { for_each_ref(mark_complete_oid, NULL); for_each_cached_alternate(mark_alternate_complete); commit_list_sort_by_date(&complete); if (cutoff) mark_recent_complete_commits(args, cutoff); } /* * Mark all complete remote refs as common refs. * Don't mark them common yet; the server has to be told so first. */ for (ref = *refs; ref; ref = ref->next) { struct object *o = deref_tag(lookup_object(ref->old_oid.hash), NULL, 0); if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE)) continue; if (!(o->flags & SEEN)) { rev_list_push((struct commit *)o, COMMON_REF | SEEN); mark_common((struct commit *)o, 1, 1); } } } filter_refs(args, refs, sought, nr_sought); for (retval = 1, ref = *refs; ref ; ref = ref->next) { const struct object_id *remote = &ref->old_oid; struct object *o; o = lookup_object(remote->hash); if (!o || !(o->flags & COMPLETE)) { retval = 0; print_verbose(args, "want %s (%s)", oid_to_hex(remote), ref->name); continue; } print_verbose(args, _("already have %s (%s)"), oid_to_hex(remote), ref->name); } save_commit_buffer = old_save_commit_buffer; return retval; }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array_entry ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; int nongit; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ prefix = setup_git_directory_gently(&nongit); gitmodules_config(); git_config(git_diff_ui_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; init_revisions(&rev, prefix); /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv, nongit, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die("Not a git repository"); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); /* * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) && check_pager_config("diff") != 0) setup_pager(); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *list = rev.pending.objects+i; struct object *obj = list->item; const char *name = list->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->sha1); obj = deref_tag(obj, NULL, 0); if (!obj) die("invalid object '%s' given.", name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { if (ARRAY_SIZE(ent) <= ents) die("more than %d trees given: '%s'", (int) ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; ents++; continue; } if (obj->type == OBJ_BLOB) { if (2 <= blobs) die("more than two blobs given: '%s'", name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; blob[blobs].mode = list->mode; blobs++; continue; } die("unhandled object '%s' given.", name); } if (rev.prune_data.nr) { if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; } /* * Now, do the arguments look reasonable? */ if (!ents) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ents == 1) result = builtin_diff_index(&rev, argc, argv); else if (ents == 2) result = builtin_diff_tree(&rev, argc, argv, ent); else if (ent[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent[0] == merge-base, * ent[ents-2] == A, and ent[ents-1] == B. Show diff * between the base and B. Note that we pick one * merge base at random if there are more than one. */ ent[1] = ent[ents-1]; result = builtin_diff_tree(&rev, argc, argv, ent); } else result = builtin_diff_combined(&rev, argc, argv, ent, ents); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = OBJECT_ARRAY_INIT; int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")), OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")), OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"), N_("only use refs matching <pattern>")), OPT_GROUP(""), OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")), OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), { /* A Hidden OPT_BOOL */ OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL, N_("dereference tags in the input (internal use)"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1, }, OPT_END(), }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); if (all + transform_stdin + !!argc > 1) { error("Specify either a list, or --all, not both!"); usage_with_options(name_rev_usage, opts); } if (all || transform_stdin) cutoff = 0; for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *object; struct commit *commit; if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); continue; } commit = NULL; object = parse_object(sha1); if (object) { struct object *peeled = deref_tag(object, *argv, 0); if (peeled && peeled->type == OBJ_COMMIT) commit = (struct commit *)peeled; } if (!object) { fprintf(stderr, "Could not get object for %s. Skipping.\n", *argv); continue; } if (commit) { if (cutoff > commit->date) cutoff = commit->date; } if (peel_tag) { if (!commit) { fprintf(stderr, "Could not get commit for %s. Skipping.\n", *argv); continue; } object = (struct object *)commit; } add_object_array(object, *argv, &revs); } if (cutoff) cutoff = cutoff - CUTOFF_DATE_SLOP; for_each_ref(name_ref, &data); if (transform_stdin) { char buffer[2048]; while (!feof(stdin)) { char *p = fgets(buffer, sizeof(buffer), stdin); if (!p) break; name_rev_line(p, &data); } } else if (all) { int i, max; max = get_max_object_index(); for (i = 0; i < max; i++) { struct object *obj = get_indexed_object(i); if (!obj || obj->type != OBJ_COMMIT) continue; show_name(obj, NULL, always, allow_undefined, data.name_only); } } else { int i; for (i = 0; i < revs.nr; i++) show_name(revs.objects[i].item, revs.objects[i].name, always, allow_undefined, data.name_only); } return 0; }
static int everything_local(struct fetch_pack_args *args, struct ref **refs, struct ref **sought, int nr_sought) { struct ref *ref; int retval; unsigned long cutoff = 0; save_commit_buffer = 0; for (ref = *refs; ref; ref = ref->next) { struct object *o; if (!has_sha1_file(ref->old_sha1)) continue; o = parse_object(ref->old_sha1); if (!o) continue; /* We already have it -- which may mean that we were * in sync with the other side at some time after * that (it is OK if we guess wrong here). */ if (o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; if (!cutoff || cutoff < commit->date) cutoff = commit->date; } } if (!args->depth) { for_each_ref(mark_complete, NULL); for_each_alternate_ref(mark_alternate_complete, NULL); commit_list_sort_by_date(&complete); if (cutoff) mark_recent_complete_commits(args, cutoff); } /* * Mark all complete remote refs as common refs. * Don't mark them common yet; the server has to be told so first. */ for (ref = *refs; ref; ref = ref->next) { struct object *o = deref_tag(lookup_object(ref->old_sha1), NULL, 0); if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE)) continue; if (!(o->flags & SEEN)) { rev_list_push((struct commit *)o, COMMON_REF | SEEN); mark_common((struct commit *)o, 1, 1); } } filter_refs(args, refs, sought, nr_sought); for (retval = 1, ref = *refs; ref ; ref = ref->next) { const unsigned char *remote = ref->old_sha1; unsigned char local[20]; struct object *o; o = lookup_object(remote); if (!o || !(o->flags & COMPLETE)) { retval = 0; if (!args->verbose) continue; fprintf(stderr, "want %s (%s)\n", sha1_to_hex(remote), ref->name); continue; } hashcpy(ref->new_sha1, local); if (!args->verbose) continue; fprintf(stderr, "already have %s (%s)\n", sha1_to_hex(remote), ref->name); } return retval; }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; struct blobinfo blob[2]; int nongit; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ prefix = setup_git_directory_gently(&nongit); gitmodules_config(); git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv, nongit, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Scale to real terminal size and respect statGraphWidth config */ rev.diffopt.stat_width = -1; rev.diffopt.stat_graph_width = -1; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); setup_diff_pager(&rev.diffopt); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree(EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *entry = &rev.pending.objects[i]; struct object *obj = entry->item; const char *name = entry->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->sha1); obj = deref_tag(obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { obj->flags |= flags; add_object_array(obj, name, &ent); } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; blob[blobs].mode = entry->mode; blobs++; } else { die(_("unhandled object '%s' given."), name); } } if (rev.prune_data.nr) paths += rev.prune_data.nr; /* * Now, do the arguments look reasonable? */ if (!ent.nr) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) result = builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[1]); else if (ent.objects[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent.objects[0] == * merge-base, ent.objects[ents-2] == A, and * ent.objects[ents-1] == B. Show diff between the * base and B. Note that we pick one merge base at * random if there are more than one. */ result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[ent.nr-1]); } else result = builtin_diff_combined(&rev, argc, argv, ent.objects, ent.nr); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }