From: Junio C Hamano Date: Tue, 13 Jun 2017 20:47:09 +0000 (-0700) Subject: Merge branch 'nd/fopen-errors' X-Git-Tag: v2.14.0-rc0~95 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b9a7d55d938a81eb6268196b789d573437492100?ds=inline;hp=-c Merge branch 'nd/fopen-errors' We often try to open a file for reading whose existence is optional, and silently ignore errors from open/fopen; report such errors if they are not due to missing files. * nd/fopen-errors: mingw_fopen: report ENOENT for invalid file names mingw: verify that paths are not mistaken for remote nicknames log: fix memory leak in open_next_file() rerere.c: move error_errno() closer to the source system call print errno when reporting a system call error wrapper.c: make warn_on_inaccessible() static wrapper.c: add and use fopen_or_warn() wrapper.c: add and use warn_on_fopen_errors() config.mak.uname: set FREAD_READS_DIRECTORIES for Darwin, too config.mak.uname: set FREAD_READS_DIRECTORIES for Linux and FreeBSD clone: use xfopen() instead of fopen() use xfopen() in more places git_fopen: fix a sparse 'not declared' warning --- b9a7d55d938a81eb6268196b789d573437492100 diff --combined bisect.c index c952df692b,bb28bf63b2..2a2b9b7267 --- a/bisect.c +++ b/bisect.c @@@ -438,10 -438,7 +438,7 @@@ static void read_bisect_paths(struct ar { struct strbuf str = STRBUF_INIT; const char *filename = git_path_bisect_names(); - FILE *fp = fopen(filename, "r"); - - if (!fp) - die_errno(_("Could not open file '%s'"), filename); + FILE *fp = xfopen(filename, "r"); while (strbuf_getline_lf(&str, fp) != EOF) { strbuf_trim(&str); @@@ -546,7 -543,7 +543,7 @@@ static unsigned get_prn(unsigned count /* * Custom integer square root from - * http://en.wikipedia.org/wiki/Integer_square_root + * https://en.wikipedia.org/wiki/Integer_square_root */ static int sqrti(int val) { @@@ -669,7 -666,7 +666,7 @@@ static int is_expected_rev(const struc if (stat(filename, &st) || !S_ISREG(st.st_mode)) return 0; - fp = fopen(filename, "r"); + fp = fopen_or_warn(filename, "r"); if (!fp) return 0; @@@ -705,7 -702,7 +702,7 @@@ static int bisect_checkout(const unsign static struct commit *get_commit_reference(const struct object_id *oid) { - struct commit *r = lookup_commit_reference(oid->hash); + struct commit *r = lookup_commit_reference(oid); if (!r) die(_("Not a valid commit name %s"), oid_to_hex(oid)); return r; @@@ -995,10 -992,8 +992,10 @@@ int bisect_next_all(const char *prefix steps_msg = xstrfmt(Q_("(roughly %d step)", "(roughly %d steps)", steps), steps); - /* TRANSLATORS: the last %s will be replaced with - "(roughly %d steps)" translation */ + /* + * TRANSLATORS: the last %s will be replaced with "(roughly %d + * steps)" translation. + */ printf(Q_("Bisecting: %d revision left to test after this %s\n", "Bisecting: %d revisions left to test after this %s\n", nr), nr, steps_msg); diff --combined builtin/am.c index 5ee146bfb3,f5dac7783e..8881d73615 --- a/builtin/am.c +++ b/builtin/am.c @@@ -879,12 -879,12 +879,12 @@@ static int hg_patch_to_mail(FILE *out, if (skip_prefix(sb.buf, "# User ", &str)) fprintf(out, "From: %s\n", str); else if (skip_prefix(sb.buf, "# Date ", &str)) { - unsigned long timestamp; + timestamp_t timestamp; long tz, tz2; char *end; errno = 0; - timestamp = strtoul(str, &end, 10); + timestamp = parse_timestamp(str, &end, 10); if (errno) return error(_("invalid timestamp")); @@@ -1145,7 -1145,7 +1145,7 @@@ static int index_has_changes(struct str DIFF_OPT_SET(&opt, EXIT_WITH_STATUS); if (!sb) DIFF_OPT_SET(&opt, QUICK); - do_diff_cache(head.hash, &opt); + do_diff_cache(&head, &opt); diffcore_std(&opt); for (i = 0; sb && i < diff_queued_diff.nr; i++) { if (i) @@@ -1275,12 -1275,8 +1275,8 @@@ static int parse_mail(struct am_state * die("BUG: invalid value for state->scissors"); } - mi.input = fopen(mail, "r"); - if (!mi.input) - die("could not open input"); - mi.output = fopen(am_path(state, "info"), "w"); - if (!mi.output) - die("could not open output 'info'"); + mi.input = xfopen(mail, "r"); + mi.output = xfopen(am_path(state, "info"), "w"); if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch"))) die("could not parse patch"); @@@ -1312,7 -1308,7 +1308,7 @@@ } if (is_empty_file(am_path(state, "patch"))) { - printf_ln(_("Patch is empty. Was it split wrong?")); + printf_ln(_("Patch is empty.")); die_user_resolve(state); } @@@ -1351,16 -1347,19 +1347,16 @@@ static int get_mail_commit_oid(struct o struct strbuf sb = STRBUF_INIT; FILE *fp = xfopen(mail, "r"); const char *x; + int ret = 0; - if (strbuf_getline_lf(&sb, fp)) - return -1; - - if (!skip_prefix(sb.buf, "From ", &x)) - return -1; - - if (get_oid_hex(x, commit_id) < 0) - return -1; + if (strbuf_getline_lf(&sb, fp) || + !skip_prefix(sb.buf, "From ", &x) || + get_oid_hex(x, commit_id) < 0) + ret = -1; strbuf_release(&sb); fclose(fp); - return 0; + return ret; } /** @@@ -1369,33 -1368,40 +1365,33 @@@ */ static void get_commit_info(struct am_state *state, struct commit *commit) { - const char *buffer, *ident_line, *author_date, *msg; + const char *buffer, *ident_line, *msg; size_t ident_len; - struct ident_split ident_split; - struct strbuf sb = STRBUF_INIT; + struct ident_split id; buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); - if (split_ident_line(&ident_split, ident_line, ident_len) < 0) { - strbuf_add(&sb, ident_line, ident_len); - die(_("invalid ident line: %s"), sb.buf); - } + if (split_ident_line(&id, ident_line, ident_len) < 0) + die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); assert(!state->author_name); - if (ident_split.name_begin) { - strbuf_add(&sb, ident_split.name_begin, - ident_split.name_end - ident_split.name_begin); - state->author_name = strbuf_detach(&sb, NULL); - } else + if (id.name_begin) + state->author_name = + xmemdupz(id.name_begin, id.name_end - id.name_begin); + else state->author_name = xstrdup(""); assert(!state->author_email); - if (ident_split.mail_begin) { - strbuf_add(&sb, ident_split.mail_begin, - ident_split.mail_end - ident_split.mail_begin); - state->author_email = strbuf_detach(&sb, NULL); - } else + if (id.mail_begin) + state->author_email = + xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); + else state->author_email = xstrdup(""); - author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL)); - strbuf_addstr(&sb, author_date); assert(!state->author_date); - state->author_date = strbuf_detach(&sb, NULL); + state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL))); assert(!state->msg); msg = strstr(buffer, "\n\n"); @@@ -1403,7 -1409,6 +1399,7 @@@ die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid)); state->msg = xstrdup(msg + 2); state->msg_len = strlen(state->msg); + unuse_commit_buffer(commit, buffer); } /** @@@ -1444,9 -1449,9 +1440,9 @@@ static void write_index_patch(const str FILE *fp; if (!get_sha1_tree("HEAD", head.hash)) - tree = lookup_tree(head.hash); + tree = lookup_tree(&head); else - tree = lookup_tree(EMPTY_TREE_SHA1_BIN); + tree = lookup_tree(&empty_tree_oid); fp = xfopen(am_path(state, "patch"), "w"); init_revisions(&rev_info, NULL); @@@ -1479,7 -1484,7 +1475,7 @@@ static int parse_mail_rebase(struct am_ if (get_mail_commit_oid(&commit_oid, mail) < 0) die(_("could not parse %s"), mail); - commit = lookup_commit_or_die(commit_oid.hash, mail); + commit = lookup_commit_or_die(&commit_oid, mail); get_commit_info(state, commit); @@@ -1609,7 -1614,7 +1605,7 @@@ static int fall_back_threeway(const str init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); - add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0); + add_pending_oid(&rev_info, "HEAD", &our_tree, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); } @@@ -1674,7 -1679,7 +1670,7 @@@ static void do_commit(const struct am_s if (!get_sha1_commit("HEAD", parent.hash)) { old_oid = &parent; - commit_list_insert(lookup_commit(parent.hash), &parents); + commit_list_insert(lookup_commit(&parent), &parents); } else { old_oid = NULL; say(state, stderr, _("applying to an empty history")); @@@ -1931,8 -1936,7 +1927,8 @@@ static void am_resolve(struct am_state if (unmerged_cache()) { printf_ln(_("You still have unmerged paths in your index.\n" - "Did you forget to use 'git add'?")); + "You should 'git add' each file with resolved conflicts to mark them as such.\n" + "You might run `git rm` on a file to accept \"deleted by them\" for it.")); die_user_resolve(state); } @@@ -2037,11 -2041,11 +2033,11 @@@ static int clean_index(const struct obj struct tree *head_tree, *remote_tree, *index_tree; struct object_id index; - head_tree = parse_tree_indirect(head->hash); + head_tree = parse_tree_indirect(head); if (!head_tree) return error(_("Could not parse object '%s'."), oid_to_hex(head)); - remote_tree = parse_tree_indirect(remote->hash); + remote_tree = parse_tree_indirect(remote); if (!remote_tree) return error(_("Could not parse object '%s'."), oid_to_hex(remote)); @@@ -2053,7 -2057,7 +2049,7 @@@ if (write_cache_as_tree(index.hash, 0, NULL)) return -1; - index_tree = parse_tree_indirect(index.hash); + index_tree = parse_tree_indirect(&index); if (!index_tree) return error(_("Could not parse object '%s'."), oid_to_hex(&index)); @@@ -2148,7 -2152,7 +2144,7 @@@ static void am_abort(struct am_state *s am_rerere_clear(); curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL); - has_curr_head = !is_null_oid(&curr_head); + has_curr_head = curr_branch && !is_null_oid(&curr_head); if (!has_curr_head) hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN); @@@ -2311,9 -2315,6 +2307,9 @@@ int cmd_am(int argc, const char **argv OPT_END() }; + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(usage, options); + git_config(git_am_config, NULL); am_state_init(&state); diff --combined builtin/blame.c index d7a2df3b47,34445d7894..749ad7f05b --- a/builtin/blame.c +++ b/builtin/blame.c @@@ -6,13 -6,21 +6,13 @@@ */ #include "cache.h" -#include "refs.h" #include "builtin.h" -#include "blob.h" #include "commit.h" -#include "tag.h" -#include "tree-walk.h" #include "diff.h" -#include "diffcore.h" #include "revision.h" #include "quote.h" -#include "xdiff-interface.h" -#include "cache-tree.h" #include "string-list.h" #include "mailmap.h" -#include "mergesort.h" #include "parse-options.h" #include "prio-queue.h" #include "utf8.h" @@@ -21,7 -29,6 +21,7 @@@ #include "line-log.h" #include "dir.h" #include "progress.h" +#include "blame.h" static char blame_usage[] = N_("git blame [] [] [] [--] "); @@@ -55,21 -62,1497 +55,21 @@@ static struct string_list mailmap = STR #define DEBUG 0 #endif -/* stats */ -static int num_read_blob; -static int num_get_patch; -static int num_commits; - -#define PICKAXE_BLAME_MOVE 01 -#define PICKAXE_BLAME_COPY 02 -#define PICKAXE_BLAME_COPY_HARDER 04 -#define PICKAXE_BLAME_COPY_HARDEST 010 - -/* - * blame for a blame_entry with score lower than these thresholds - * is not passed to the parent using move/copy logic. - */ -static unsigned blame_move_score; -static unsigned blame_copy_score; -#define BLAME_DEFAULT_MOVE_SCORE 20 -#define BLAME_DEFAULT_COPY_SCORE 40 - -/* Remember to update object flag allocation in object.h */ -#define METAINFO_SHOWN (1u<<12) -#define MORE_THAN_ONE_PATH (1u<<13) - -/* - * One blob in a commit that is being suspected - */ -struct origin { - int refcnt; - /* Record preceding blame record for this blob */ - struct origin *previous; - /* origins are put in a list linked via `next' hanging off the - * corresponding commit's util field in order to make finding - * them fast. The presence in this chain does not count - * towards the origin's reference count. It is tempting to - * let it count as long as the commit is pending examination, - * but even under circumstances where the commit will be - * present multiple times in the priority queue of unexamined - * commits, processing the first instance will not leave any - * work requiring the origin data for the second instance. An - * interspersed commit changing that would have to be - * preexisting with a different ancestry and with the same - * commit date in order to wedge itself between two instances - * of the same commit in the priority queue _and_ produce - * blame entries relevant for it. While we don't want to let - * us get tripped up by this case, it certainly does not seem - * worth optimizing for. - */ - struct origin *next; - struct commit *commit; - /* `suspects' contains blame entries that may be attributed to - * this origin's commit or to parent commits. When a commit - * is being processed, all suspects will be moved, either by - * assigning them to an origin in a different commit, or by - * shipping them to the scoreboard's ent list because they - * cannot be attributed to a different commit. - */ - struct blame_entry *suspects; - mmfile_t file; - struct object_id blob_oid; - unsigned mode; - /* guilty gets set when shipping any suspects to the final - * blame list instead of other commits - */ - char guilty; - char path[FLEX_ARRAY]; -}; - -struct progress_info { - struct progress *progress; - int blamed_lines; -}; - -static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, - xdl_emit_hunk_consume_func_t hunk_func, void *cb_data) -{ - xpparam_t xpp = {0}; - xdemitconf_t xecfg = {0}; - xdemitcb_t ecb = {NULL}; - - xpp.flags = xdl_opts; - xecfg.hunk_func = hunk_func; - ecb.priv = cb_data; - return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb); -} - -/* - * Prepare diff_filespec and convert it using diff textconv API - * if the textconv driver exists. - * Return 1 if the conversion succeeds, 0 otherwise. - */ -int textconv_object(const char *path, - unsigned mode, - const struct object_id *oid, - int oid_valid, - char **buf, - unsigned long *buf_size) -{ - struct diff_filespec *df; - struct userdiff_driver *textconv; - - df = alloc_filespec(path); - fill_filespec(df, oid->hash, oid_valid, mode); - textconv = get_textconv(df); - if (!textconv) { - free_filespec(df); - return 0; - } - - *buf_size = fill_textconv(textconv, df, buf); - free_filespec(df); - return 1; -} - -/* - * Given an origin, prepare mmfile_t structure to be used by the - * diff machinery - */ -static void fill_origin_blob(struct diff_options *opt, - struct origin *o, mmfile_t *file) -{ - if (!o->file.ptr) { - enum object_type type; - unsigned long file_size; - - num_read_blob++; - if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size)) - ; - else - file->ptr = read_sha1_file(o->blob_oid.hash, &type, - &file_size); - file->size = file_size; - - if (!file->ptr) - die("Cannot read blob %s for path %s", - oid_to_hex(&o->blob_oid), - o->path); - o->file = *file; - } - else - *file = o->file; -} - -/* - * Origin is refcounted and usually we keep the blob contents to be - * reused. - */ -static inline struct origin *origin_incref(struct origin *o) -{ - if (o) - o->refcnt++; - return o; -} - -static void origin_decref(struct origin *o) -{ - if (o && --o->refcnt <= 0) { - struct origin *p, *l = NULL; - if (o->previous) - origin_decref(o->previous); - free(o->file.ptr); - /* Should be present exactly once in commit chain */ - for (p = o->commit->util; p; l = p, p = p->next) { - if (p == o) { - if (l) - l->next = p->next; - else - o->commit->util = p->next; - free(o); - return; - } - } - die("internal error in blame::origin_decref"); - } -} - -static void drop_origin_blob(struct origin *o) -{ - if (o->file.ptr) { - free(o->file.ptr); - o->file.ptr = NULL; - } -} - -/* - * Each group of lines is described by a blame_entry; it can be split - * as we pass blame to the parents. They are arranged in linked lists - * kept as `suspects' of some unprocessed origin, or entered (when the - * blame origin has been finalized) into the scoreboard structure. - * While the scoreboard structure is only sorted at the end of - * processing (according to final image line number), the lists - * attached to an origin are sorted by the target line number. - */ -struct blame_entry { - struct blame_entry *next; - - /* the first line of this group in the final image; - * internally all line numbers are 0 based. - */ - int lno; - - /* how many lines this group has */ - int num_lines; - - /* the commit that introduced this group into the final image */ - struct origin *suspect; - - /* the line number of the first line of this group in the - * suspect's file; internally all line numbers are 0 based. - */ - int s_lno; - - /* how significant this entry is -- cached to avoid - * scanning the lines over and over. - */ - unsigned score; -}; - -/* - * Any merge of blames happens on lists of blames that arrived via - * different parents in a single suspect. In this case, we want to - * sort according to the suspect line numbers as opposed to the final - * image line numbers. The function body is somewhat longish because - * it avoids unnecessary writes. - */ - -static struct blame_entry *blame_merge(struct blame_entry *list1, - struct blame_entry *list2) -{ - struct blame_entry *p1 = list1, *p2 = list2, - **tail = &list1; - - if (!p1) - return p2; - if (!p2) - return p1; - - if (p1->s_lno <= p2->s_lno) { - do { - tail = &p1->next; - if ((p1 = *tail) == NULL) { - *tail = p2; - return list1; - } - } while (p1->s_lno <= p2->s_lno); - } - for (;;) { - *tail = p2; - do { - tail = &p2->next; - if ((p2 = *tail) == NULL) { - *tail = p1; - return list1; - } - } while (p1->s_lno > p2->s_lno); - *tail = p1; - do { - tail = &p1->next; - if ((p1 = *tail) == NULL) { - *tail = p2; - return list1; - } - } while (p1->s_lno <= p2->s_lno); - } -} - -static void *get_next_blame(const void *p) -{ - return ((struct blame_entry *)p)->next; -} - -static void set_next_blame(void *p1, void *p2) -{ - ((struct blame_entry *)p1)->next = p2; -} - -/* - * Final image line numbers are all different, so we don't need a - * three-way comparison here. - */ - -static int compare_blame_final(const void *p1, const void *p2) -{ - return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno - ? 1 : -1; -} - -static int compare_blame_suspect(const void *p1, const void *p2) -{ - const struct blame_entry *s1 = p1, *s2 = p2; - /* - * to allow for collating suspects, we sort according to the - * respective pointer value as the primary sorting criterion. - * The actual relation is pretty unimportant as long as it - * establishes a total order. Comparing as integers gives us - * that. - */ - if (s1->suspect != s2->suspect) - return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1; - if (s1->s_lno == s2->s_lno) - return 0; - return s1->s_lno > s2->s_lno ? 1 : -1; -} - -static struct blame_entry *blame_sort(struct blame_entry *head, - int (*compare_fn)(const void *, const void *)) -{ - return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn); -} - -static int compare_commits_by_reverse_commit_date(const void *a, - const void *b, - void *c) -{ - return -compare_commits_by_commit_date(a, b, c); -} - -/* - * The current state of the blame assignment. - */ -struct scoreboard { - /* the final commit (i.e. where we started digging from) */ - struct commit *final; - /* Priority queue for commits with unassigned blame records */ - struct prio_queue commits; - struct rev_info *revs; - const char *path; - - /* - * The contents in the final image. - * Used by many functions to obtain contents of the nth line, - * indexed with scoreboard.lineno[blame_entry.lno]. - */ - const char *final_buf; - unsigned long final_buf_size; - - /* linked list of blames */ - struct blame_entry *ent; - - /* look-up a line in the final buffer */ - int num_lines; - int *lineno; -}; - -static void sanity_check_refcnt(struct scoreboard *); - -/* - * If two blame entries that are next to each other came from - * contiguous lines in the same origin (i.e. pair), - * merge them together. - */ -static void coalesce(struct scoreboard *sb) -{ - struct blame_entry *ent, *next; - - for (ent = sb->ent; ent && (next = ent->next); ent = next) { - if (ent->suspect == next->suspect && - ent->s_lno + ent->num_lines == next->s_lno) { - ent->num_lines += next->num_lines; - ent->next = next->next; - origin_decref(next->suspect); - free(next); - ent->score = 0; - next = ent; /* again */ - } - } - - if (DEBUG) /* sanity */ - sanity_check_refcnt(sb); -} - -/* - * Merge the given sorted list of blames into a preexisting origin. - * If there were no previous blames to that commit, it is entered into - * the commit priority queue of the score board. - */ - -static void queue_blames(struct scoreboard *sb, struct origin *porigin, - struct blame_entry *sorted) -{ - if (porigin->suspects) - porigin->suspects = blame_merge(porigin->suspects, sorted); - else { - struct origin *o; - for (o = porigin->commit->util; o; o = o->next) { - if (o->suspects) { - porigin->suspects = sorted; - return; - } - } - porigin->suspects = sorted; - prio_queue_put(&sb->commits, porigin->commit); - } -} - -/* - * Given a commit and a path in it, create a new origin structure. - * The callers that add blame to the scoreboard should use - * get_origin() to obtain shared, refcounted copy instead of calling - * this function directly. - */ -static struct origin *make_origin(struct commit *commit, const char *path) -{ - struct origin *o; - FLEX_ALLOC_STR(o, path, path); - o->commit = commit; - o->refcnt = 1; - o->next = commit->util; - commit->util = o; - return o; -} - -/* - * Locate an existing origin or create a new one. - * This moves the origin to front position in the commit util list. - */ -static struct origin *get_origin(struct scoreboard *sb, - struct commit *commit, - const char *path) -{ - struct origin *o, *l; - - for (o = commit->util, l = NULL; o; l = o, o = o->next) { - if (!strcmp(o->path, path)) { - /* bump to front */ - if (l) { - l->next = o->next; - o->next = commit->util; - commit->util = o; - } - return origin_incref(o); - } - } - return make_origin(commit, path); -} - -/* - * Fill the blob_sha1 field of an origin if it hasn't, so that later - * call to fill_origin_blob() can use it to locate the data. blob_sha1 - * for an origin is also used to pass the blame for the entire file to - * the parent to detect the case where a child's blob is identical to - * that of its parent's. - * - * This also fills origin->mode for corresponding tree path. - */ -static int fill_blob_sha1_and_mode(struct origin *origin) -{ - if (!is_null_oid(&origin->blob_oid)) - return 0; - if (get_tree_entry(origin->commit->object.oid.hash, - origin->path, - origin->blob_oid.hash, &origin->mode)) - goto error_out; - if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB) - goto error_out; - return 0; - error_out: - oidclr(&origin->blob_oid); - origin->mode = S_IFINVALID; - return -1; -} - -/* - * We have an origin -- check if the same path exists in the - * parent and return an origin structure to represent it. - */ -static struct origin *find_origin(struct scoreboard *sb, - struct commit *parent, - struct origin *origin) -{ - struct origin *porigin; - struct diff_options diff_opts; - const char *paths[2]; - - /* First check any existing origins */ - for (porigin = parent->util; porigin; porigin = porigin->next) - if (!strcmp(porigin->path, origin->path)) { - /* - * The same path between origin and its parent - * without renaming -- the most common case. - */ - return origin_incref (porigin); - } - - /* See if the origin->path is different between parent - * and origin first. Most of the time they are the - * same and diff-tree is fairly efficient about this. - */ - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.detect_rename = 0; - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - paths[0] = origin->path; - paths[1] = NULL; - - parse_pathspec(&diff_opts.pathspec, - PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, - PATHSPEC_LITERAL_PATH, "", paths); - diff_setup_done(&diff_opts); - - if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); - diffcore_std(&diff_opts); - - if (!diff_queued_diff.nr) { - /* The path is the same as parent */ - porigin = get_origin(sb, parent, origin->path); - oidcpy(&porigin->blob_oid, &origin->blob_oid); - porigin->mode = origin->mode; - } else { - /* - * Since origin->path is a pathspec, if the parent - * commit had it as a directory, we will see a whole - * bunch of deletion of files in the directory that we - * do not care about. - */ - int i; - struct diff_filepair *p = NULL; - for (i = 0; i < diff_queued_diff.nr; i++) { - const char *name; - p = diff_queued_diff.queue[i]; - name = p->one->path ? p->one->path : p->two->path; - if (!strcmp(name, origin->path)) - break; - } - if (!p) - die("internal error in blame::find_origin"); - switch (p->status) { - default: - die("internal error in blame::find_origin (%c)", - p->status); - case 'M': - porigin = get_origin(sb, parent, origin->path); - oidcpy(&porigin->blob_oid, &p->one->oid); - porigin->mode = p->one->mode; - break; - case 'A': - case 'T': - /* Did not exist in parent, or type changed */ - break; - } - } - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); - return porigin; -} - -/* - * We have an origin -- find the path that corresponds to it in its - * parent and return an origin structure to represent it. - */ -static struct origin *find_rename(struct scoreboard *sb, - struct commit *parent, - struct origin *origin) -{ - struct origin *porigin = NULL; - struct diff_options diff_opts; - int i; - - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.detect_rename = DIFF_DETECT_RENAME; - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_opts.single_follow = origin->path; - diff_setup_done(&diff_opts); - - if (is_null_oid(&origin->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - origin->commit->tree->object.oid.hash, - "", &diff_opts); - diffcore_std(&diff_opts); - - for (i = 0; i < diff_queued_diff.nr; i++) { - struct diff_filepair *p = diff_queued_diff.queue[i]; - if ((p->status == 'R' || p->status == 'C') && - !strcmp(p->two->path, origin->path)) { - porigin = get_origin(sb, parent, p->one->path); - oidcpy(&porigin->blob_oid, &p->one->oid); - porigin->mode = p->one->mode; - break; - } - } - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); - return porigin; -} - -/* - * Append a new blame entry to a given output queue. - */ -static void add_blame_entry(struct blame_entry ***queue, - const struct blame_entry *src) -{ - struct blame_entry *e = xmalloc(sizeof(*e)); - memcpy(e, src, sizeof(*e)); - origin_incref(e->suspect); - - e->next = **queue; - **queue = e; - *queue = &e->next; -} - -/* - * src typically is on-stack; we want to copy the information in it to - * a malloced blame_entry that gets added to the given queue. The - * origin of dst loses a refcnt. - */ -static void dup_entry(struct blame_entry ***queue, - struct blame_entry *dst, struct blame_entry *src) -{ - origin_incref(src->suspect); - origin_decref(dst->suspect); - memcpy(dst, src, sizeof(*src)); - dst->next = **queue; - **queue = dst; - *queue = &dst->next; -} - -static const char *nth_line(struct scoreboard *sb, long lno) -{ - return sb->final_buf + sb->lineno[lno]; -} - -static const char *nth_line_cb(void *data, long lno) -{ - return nth_line((struct scoreboard *)data, lno); -} - -/* - * It is known that lines between tlno to same came from parent, and e - * has an overlap with that range. it also is known that parent's - * line plno corresponds to e's line tlno. - * - * <---- e -----> - * <------> - * <------------> - * <------------> - * <------------------> - * - * Split e into potentially three parts; before this chunk, the chunk - * to be blamed for the parent, and after that portion. - */ -static void split_overlap(struct blame_entry *split, - struct blame_entry *e, - int tlno, int plno, int same, - struct origin *parent) -{ - int chunk_end_lno; - memset(split, 0, sizeof(struct blame_entry [3])); - - if (e->s_lno < tlno) { - /* there is a pre-chunk part not blamed on parent */ - split[0].suspect = origin_incref(e->suspect); - split[0].lno = e->lno; - split[0].s_lno = e->s_lno; - split[0].num_lines = tlno - e->s_lno; - split[1].lno = e->lno + tlno - e->s_lno; - split[1].s_lno = plno; - } - else { - split[1].lno = e->lno; - split[1].s_lno = plno + (e->s_lno - tlno); - } - - if (same < e->s_lno + e->num_lines) { - /* there is a post-chunk part not blamed on parent */ - split[2].suspect = origin_incref(e->suspect); - split[2].lno = e->lno + (same - e->s_lno); - split[2].s_lno = e->s_lno + (same - e->s_lno); - split[2].num_lines = e->s_lno + e->num_lines - same; - chunk_end_lno = split[2].lno; - } - else - chunk_end_lno = e->lno + e->num_lines; - split[1].num_lines = chunk_end_lno - split[1].lno; - - /* - * if it turns out there is nothing to blame the parent for, - * forget about the splitting. !split[1].suspect signals this. - */ - if (split[1].num_lines < 1) - return; - split[1].suspect = origin_incref(parent); -} - -/* - * split_overlap() divided an existing blame e into up to three parts - * in split. Any assigned blame is moved to queue to - * reflect the split. - */ -static void split_blame(struct blame_entry ***blamed, - struct blame_entry ***unblamed, - struct blame_entry *split, - struct blame_entry *e) -{ - if (split[0].suspect && split[2].suspect) { - /* The first part (reuse storage for the existing entry e) */ - dup_entry(unblamed, e, &split[0]); - - /* The last part -- me */ - add_blame_entry(unblamed, &split[2]); - - /* ... and the middle part -- parent */ - add_blame_entry(blamed, &split[1]); - } - else if (!split[0].suspect && !split[2].suspect) - /* - * The parent covers the entire area; reuse storage for - * e and replace it with the parent. - */ - dup_entry(blamed, e, &split[1]); - else if (split[0].suspect) { - /* me and then parent */ - dup_entry(unblamed, e, &split[0]); - add_blame_entry(blamed, &split[1]); - } - else { - /* parent and then me */ - dup_entry(blamed, e, &split[1]); - add_blame_entry(unblamed, &split[2]); - } -} - -/* - * After splitting the blame, the origins used by the - * on-stack blame_entry should lose one refcnt each. - */ -static void decref_split(struct blame_entry *split) -{ - int i; - - for (i = 0; i < 3; i++) - origin_decref(split[i].suspect); -} - -/* - * reverse_blame reverses the list given in head, appending tail. - * That allows us to build lists in reverse order, then reverse them - * afterwards. This can be faster than building the list in proper - * order right away. The reason is that building in proper order - * requires writing a link in the _previous_ element, while building - * in reverse order just requires placing the list head into the - * _current_ element. - */ - -static struct blame_entry *reverse_blame(struct blame_entry *head, - struct blame_entry *tail) -{ - while (head) { - struct blame_entry *next = head->next; - head->next = tail; - tail = head; - head = next; - } - return tail; -} - -/* - * Process one hunk from the patch between the current suspect for - * blame_entry e and its parent. This first blames any unfinished - * entries before the chunk (which is where target and parent start - * differing) on the parent, and then splits blame entries at the - * start and at the end of the difference region. Since use of -M and - * -C options may lead to overlapping/duplicate source line number - * ranges, all we can rely on from sorting/merging is the order of the - * first suspect line number. - */ -static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, - int tlno, int offset, int same, - struct origin *parent) -{ - struct blame_entry *e = **srcq; - struct blame_entry *samep = NULL, *diffp = NULL; - - while (e && e->s_lno < tlno) { - struct blame_entry *next = e->next; - /* - * current record starts before differing portion. If - * it reaches into it, we need to split it up and - * examine the second part separately. - */ - if (e->s_lno + e->num_lines > tlno) { - /* Move second half to a new record */ - int len = tlno - e->s_lno; - struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); - n->suspect = e->suspect; - n->lno = e->lno + len; - n->s_lno = e->s_lno + len; - n->num_lines = e->num_lines - len; - e->num_lines = len; - e->score = 0; - /* Push new record to diffp */ - n->next = diffp; - diffp = n; - } else - origin_decref(e->suspect); - /* Pass blame for everything before the differing - * chunk to the parent */ - e->suspect = origin_incref(parent); - e->s_lno += offset; - e->next = samep; - samep = e; - e = next; - } - /* - * As we don't know how much of a common stretch after this - * diff will occur, the currently blamed parts are all that we - * can assign to the parent for now. - */ - - if (samep) { - **dstq = reverse_blame(samep, **dstq); - *dstq = &samep->next; - } - /* - * Prepend the split off portions: everything after e starts - * after the blameable portion. - */ - e = reverse_blame(diffp, e); - - /* - * Now retain records on the target while parts are different - * from the parent. - */ - samep = NULL; - diffp = NULL; - while (e && e->s_lno < same) { - struct blame_entry *next = e->next; - - /* - * If current record extends into sameness, need to split. - */ - if (e->s_lno + e->num_lines > same) { - /* - * Move second half to a new record to be - * processed by later chunks - */ - int len = same - e->s_lno; - struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry)); - n->suspect = origin_incref(e->suspect); - n->lno = e->lno + len; - n->s_lno = e->s_lno + len; - n->num_lines = e->num_lines - len; - e->num_lines = len; - e->score = 0; - /* Push new record to samep */ - n->next = samep; - samep = n; - } - e->next = diffp; - diffp = e; - e = next; - } - **srcq = reverse_blame(diffp, reverse_blame(samep, e)); - /* Move across elements that are in the unblamable portion */ - if (diffp) - *srcq = &diffp->next; -} - -struct blame_chunk_cb_data { - struct origin *parent; - long offset; - struct blame_entry **dstq; - struct blame_entry **srcq; -}; - -/* diff chunks are from parent to target */ -static int blame_chunk_cb(long start_a, long count_a, - long start_b, long count_b, void *data) -{ - struct blame_chunk_cb_data *d = data; - if (start_a - start_b != d->offset) - die("internal error in blame::blame_chunk_cb"); - blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b, - start_b + count_b, d->parent); - d->offset = start_a + count_a - (start_b + count_b); - return 0; -} - -/* - * We are looking at the origin 'target' and aiming to pass blame - * for the lines it is suspected to its parent. Run diff to find - * which lines came from parent and pass blame for them. - */ -static void pass_blame_to_parent(struct scoreboard *sb, - struct origin *target, - struct origin *parent) -{ - mmfile_t file_p, file_o; - struct blame_chunk_cb_data d; - struct blame_entry *newdest = NULL; - - if (!target->suspects) - return; /* nothing remains for this target */ - - d.parent = parent; - d.offset = 0; - d.dstq = &newdest; d.srcq = &target->suspects; - - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); - fill_origin_blob(&sb->revs->diffopt, target, &file_o); - num_get_patch++; - - if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d)) - die("unable to generate diff (%s -> %s)", - oid_to_hex(&parent->commit->object.oid), - oid_to_hex(&target->commit->object.oid)); - /* The rest are the same as the parent */ - blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent); - *d.dstq = NULL; - queue_blames(sb, parent, newdest); - - return; -} - -/* - * The lines in blame_entry after splitting blames many times can become - * very small and trivial, and at some point it becomes pointless to - * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any - * ordinary C program, and it is not worth to say it was copied from - * totally unrelated file in the parent. - * - * Compute how trivial the lines in the blame_entry are. - */ -static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e) -{ - unsigned score; - const char *cp, *ep; - - if (e->score) - return e->score; - - score = 1; - cp = nth_line(sb, e->lno); - ep = nth_line(sb, e->lno + e->num_lines); - while (cp < ep) { - unsigned ch = *((unsigned char *)cp); - if (isalnum(ch)) - score++; - cp++; - } - e->score = score; - return score; -} - -/* - * best_so_far[] and this[] are both a split of an existing blame_entry - * that passes blame to the parent. Maintain best_so_far the best split - * so far, by comparing this and best_so_far and copying this into - * bst_so_far as needed. - */ -static void copy_split_if_better(struct scoreboard *sb, - struct blame_entry *best_so_far, - struct blame_entry *this) -{ - int i; - - if (!this[1].suspect) - return; - if (best_so_far[1].suspect) { - if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1])) - return; - } - - for (i = 0; i < 3; i++) - origin_incref(this[i].suspect); - decref_split(best_so_far); - memcpy(best_so_far, this, sizeof(struct blame_entry [3])); -} - -/* - * We are looking at a part of the final image represented by - * ent (tlno and same are offset by ent->s_lno). - * tlno is where we are looking at in the final image. - * up to (but not including) same match preimage. - * plno is where we are looking at in the preimage. - * - * <-------------- final image ----------------------> - * <------ent------> - * ^tlno ^same - * <---------preimage-----> - * ^plno - * - * All line numbers are 0-based. - */ -static void handle_split(struct scoreboard *sb, - struct blame_entry *ent, - int tlno, int plno, int same, - struct origin *parent, - struct blame_entry *split) -{ - if (ent->num_lines <= tlno) - return; - if (tlno < same) { - struct blame_entry this[3]; - tlno += ent->s_lno; - same += ent->s_lno; - split_overlap(this, ent, tlno, plno, same, parent); - copy_split_if_better(sb, split, this); - decref_split(this); - } -} - -struct handle_split_cb_data { - struct scoreboard *sb; - struct blame_entry *ent; - struct origin *parent; - struct blame_entry *split; - long plno; - long tlno; -}; - -static int handle_split_cb(long start_a, long count_a, - long start_b, long count_b, void *data) -{ - struct handle_split_cb_data *d = data; - handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent, - d->split); - d->plno = start_a + count_a; - d->tlno = start_b + count_b; - return 0; -} - -/* - * Find the lines from parent that are the same as ent so that - * we can pass blames to it. file_p has the blob contents for - * the parent. - */ -static void find_copy_in_blob(struct scoreboard *sb, - struct blame_entry *ent, - struct origin *parent, - struct blame_entry *split, - mmfile_t *file_p) -{ - const char *cp; - mmfile_t file_o; - struct handle_split_cb_data d; - - memset(&d, 0, sizeof(d)); - d.sb = sb; d.ent = ent; d.parent = parent; d.split = split; - /* - * Prepare mmfile that contains only the lines in ent. - */ - cp = nth_line(sb, ent->lno); - file_o.ptr = (char *) cp; - file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp; - - /* - * file_o is a part of final image we are annotating. - * file_p partially may match that image. - */ - memset(split, 0, sizeof(struct blame_entry [3])); - if (diff_hunks(file_p, &file_o, handle_split_cb, &d)) - die("unable to generate diff (%s)", - oid_to_hex(&parent->commit->object.oid)); - /* remainder, if any, all match the preimage */ - handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split); -} - -/* Move all blame entries from list *source that have a score smaller - * than score_min to the front of list *small. - * Returns a pointer to the link pointing to the old head of the small list. - */ - -static struct blame_entry **filter_small(struct scoreboard *sb, - struct blame_entry **small, - struct blame_entry **source, - unsigned score_min) -{ - struct blame_entry *p = *source; - struct blame_entry *oldsmall = *small; - while (p) { - if (ent_score(sb, p) <= score_min) { - *small = p; - small = &p->next; - p = *small; - } else { - *source = p; - source = &p->next; - p = *source; - } - } - *small = oldsmall; - *source = NULL; - return small; -} - -/* - * See if lines currently target is suspected for can be attributed to - * parent. - */ -static void find_move_in_parent(struct scoreboard *sb, - struct blame_entry ***blamed, - struct blame_entry **toosmall, - struct origin *target, - struct origin *parent) -{ - struct blame_entry *e, split[3]; - struct blame_entry *unblamed = target->suspects; - struct blame_entry *leftover = NULL; - mmfile_t file_p; - - if (!unblamed) - return; /* nothing remains for this target */ - - fill_origin_blob(&sb->revs->diffopt, parent, &file_p); - if (!file_p.ptr) - return; +static unsigned blame_move_score; +static unsigned blame_copy_score; - /* At each iteration, unblamed has a NULL-terminated list of - * entries that have not yet been tested for blame. leftover - * contains the reversed list of entries that have been tested - * without being assignable to the parent. - */ - do { - struct blame_entry **unblamedtail = &unblamed; - struct blame_entry *next; - for (e = unblamed; e; e = next) { - next = e->next; - find_copy_in_blob(sb, e, parent, split, &file_p); - if (split[1].suspect && - blame_move_score < ent_score(sb, &split[1])) { - split_blame(blamed, &unblamedtail, split, e); - } else { - e->next = leftover; - leftover = e; - } - decref_split(split); - } - *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score); - } while (unblamed); - target->suspects = reverse_blame(leftover, NULL); -} +/* Remember to update object flag allocation in object.h */ +#define METAINFO_SHOWN (1u<<12) +#define MORE_THAN_ONE_PATH (1u<<13) -struct blame_list { - struct blame_entry *ent; - struct blame_entry split[3]; +struct progress_info { + struct progress *progress; + int blamed_lines; }; -/* - * Count the number of entries the target is suspected for, - * and prepare a list of entry and the best split. - */ -static struct blame_list *setup_blame_list(struct blame_entry *unblamed, - int *num_ents_p) -{ - struct blame_entry *e; - int num_ents, i; - struct blame_list *blame_list = NULL; - - for (e = unblamed, num_ents = 0; e; e = e->next) - num_ents++; - if (num_ents) { - blame_list = xcalloc(num_ents, sizeof(struct blame_list)); - for (e = unblamed, i = 0; e; e = e->next) - blame_list[i++].ent = e; - } - *num_ents_p = num_ents; - return blame_list; -} - -/* - * For lines target is suspected for, see if we can find code movement - * across file boundary from the parent commit. porigin is the path - * in the parent we already tried. - */ -static void find_copy_in_parent(struct scoreboard *sb, - struct blame_entry ***blamed, - struct blame_entry **toosmall, - struct origin *target, - struct commit *parent, - struct origin *porigin, - int opt) -{ - struct diff_options diff_opts; - int i, j; - struct blame_list *blame_list; - int num_ents; - struct blame_entry *unblamed = target->suspects; - struct blame_entry *leftover = NULL; - - if (!unblamed) - return; /* nothing remains for this target */ - - diff_setup(&diff_opts); - DIFF_OPT_SET(&diff_opts, RECURSIVE); - diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - - diff_setup_done(&diff_opts); - - /* Try "find copies harder" on new path if requested; - * we do not want to use diffcore_rename() actually to - * match things up; find_copies_harder is set only to - * force diff_tree_sha1() to feed all filepairs to diff_queue, - * and this code needs to be after diff_setup_done(), which - * usually makes find-copies-harder imply copy detection. - */ - if ((opt & PICKAXE_BLAME_COPY_HARDEST) - || ((opt & PICKAXE_BLAME_COPY_HARDER) - && (!porigin || strcmp(target->path, porigin->path)))) - DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); - - if (is_null_oid(&target->commit->object.oid)) - do_diff_cache(parent->tree->object.oid.hash, &diff_opts); - else - diff_tree_sha1(parent->tree->object.oid.hash, - target->commit->tree->object.oid.hash, - "", &diff_opts); - - if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER)) - diffcore_std(&diff_opts); - - do { - struct blame_entry **unblamedtail = &unblamed; - blame_list = setup_blame_list(unblamed, &num_ents); - - for (i = 0; i < diff_queued_diff.nr; i++) { - struct diff_filepair *p = diff_queued_diff.queue[i]; - struct origin *norigin; - mmfile_t file_p; - struct blame_entry this[3]; - - if (!DIFF_FILE_VALID(p->one)) - continue; /* does not exist in parent */ - if (S_ISGITLINK(p->one->mode)) - continue; /* ignore git links */ - if (porigin && !strcmp(p->one->path, porigin->path)) - /* find_move already dealt with this path */ - continue; - - norigin = get_origin(sb, parent, p->one->path); - oidcpy(&norigin->blob_oid, &p->one->oid); - norigin->mode = p->one->mode; - fill_origin_blob(&sb->revs->diffopt, norigin, &file_p); - if (!file_p.ptr) - continue; - - for (j = 0; j < num_ents; j++) { - find_copy_in_blob(sb, blame_list[j].ent, - norigin, this, &file_p); - copy_split_if_better(sb, blame_list[j].split, - this); - decref_split(this); - } - origin_decref(norigin); - } - - for (j = 0; j < num_ents; j++) { - struct blame_entry *split = blame_list[j].split; - if (split[1].suspect && - blame_copy_score < ent_score(sb, &split[1])) { - split_blame(blamed, &unblamedtail, split, - blame_list[j].ent); - } else { - blame_list[j].ent->next = leftover; - leftover = blame_list[j].ent; - } - decref_split(split); - } - free(blame_list); - *unblamedtail = NULL; - toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score); - } while (unblamed); - target->suspects = reverse_blame(leftover, NULL); - diff_flush(&diff_opts); - clear_pathspec(&diff_opts.pathspec); -} - -/* - * The blobs of origin and porigin exactly match, so everything - * origin is suspected for can be blamed on the parent. - */ -static void pass_whole_blame(struct scoreboard *sb, - struct origin *origin, struct origin *porigin) -{ - struct blame_entry *e, *suspects; - - if (!porigin->file.ptr && origin->file.ptr) { - /* Steal its file */ - porigin->file = origin->file; - origin->file.ptr = NULL; - } - suspects = origin->suspects; - origin->suspects = NULL; - for (e = suspects; e; e = e->next) { - origin_incref(porigin); - origin_decref(e->suspect); - e->suspect = porigin; - } - queue_blames(sb, porigin, suspects); -} - -/* - * We pass blame from the current commit to its parents. We keep saying - * "parent" (and "porigin"), but what we mean is to find scapegoat to - * exonerate ourselves. - */ -static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit) -{ - if (!reverse) { - if (revs->first_parent_only && - commit->parents && - commit->parents->next) { - free_commit_list(commit->parents->next); - commit->parents->next = NULL; - } - return commit->parents; - } - return lookup_decoration(&revs->children, &commit->object); -} - -static int num_scapegoats(struct rev_info *revs, struct commit *commit) -{ - struct commit_list *l = first_scapegoat(revs, commit); - return commit_list_count(l); -} - -/* Distribute collected unsorted blames to the respected sorted lists - * in the various origins. - */ -static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed) -{ - blamed = blame_sort(blamed, compare_blame_suspect); - while (blamed) - { - struct origin *porigin = blamed->suspect; - struct blame_entry *suspects = NULL; - do { - struct blame_entry *next = blamed->next; - blamed->next = suspects; - suspects = blamed; - blamed = next; - } while (blamed && blamed->suspect == porigin); - suspects = reverse_blame(suspects, NULL); - queue_blames(sb, porigin, suspects); - } -} - -#define MAXSG 16 - -static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) +static const char *nth_line_cb(void *data, long lno) { - struct rev_info *revs = sb->revs; - int i, pass, num_sg; - struct commit *commit = origin->commit; - struct commit_list *sg; - struct origin *sg_buf[MAXSG]; - struct origin *porigin, **sg_origin = sg_buf; - struct blame_entry *toosmall = NULL; - struct blame_entry *blames, **blametail = &blames; - - num_sg = num_scapegoats(revs, commit); - if (!num_sg) - goto finish; - else if (num_sg < ARRAY_SIZE(sg_buf)) - memset(sg_buf, 0, sizeof(sg_buf)); - else - sg_origin = xcalloc(num_sg, sizeof(*sg_origin)); - - /* - * The first pass looks for unrenamed path to optimize for - * common cases, then we look for renames in the second pass. - */ - for (pass = 0; pass < 2 - no_whole_file_rename; pass++) { - struct origin *(*find)(struct scoreboard *, - struct commit *, struct origin *); - find = pass ? find_rename : find_origin; - - for (i = 0, sg = first_scapegoat(revs, commit); - i < num_sg && sg; - sg = sg->next, i++) { - struct commit *p = sg->item; - int j, same; - - if (sg_origin[i]) - continue; - if (parse_commit(p)) - continue; - porigin = find(sb, p, origin); - if (!porigin) - continue; - if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) { - pass_whole_blame(sb, origin, porigin); - origin_decref(porigin); - goto finish; - } - for (j = same = 0; j < i; j++) - if (sg_origin[j] && - !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) { - same = 1; - break; - } - if (!same) - sg_origin[i] = porigin; - else - origin_decref(porigin); - } - } - - num_commits++; - for (i = 0, sg = first_scapegoat(revs, commit); - i < num_sg && sg; - sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; - if (!porigin) - continue; - if (!origin->previous) { - origin_incref(porigin); - origin->previous = porigin; - } - pass_blame_to_parent(sb, origin, porigin); - if (!origin->suspects) - goto finish; - } - - /* - * Optionally find moves in parents' files. - */ - if (opt & PICKAXE_BLAME_MOVE) { - filter_small(sb, &toosmall, &origin->suspects, blame_move_score); - if (origin->suspects) { - for (i = 0, sg = first_scapegoat(revs, commit); - i < num_sg && sg; - sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; - if (!porigin) - continue; - find_move_in_parent(sb, &blametail, &toosmall, origin, porigin); - if (!origin->suspects) - break; - } - } - } - - /* - * Optionally find copies from parents' files. - */ - if (opt & PICKAXE_BLAME_COPY) { - if (blame_copy_score > blame_move_score) - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); - else if (blame_copy_score < blame_move_score) { - origin->suspects = blame_merge(origin->suspects, toosmall); - toosmall = NULL; - filter_small(sb, &toosmall, &origin->suspects, blame_copy_score); - } - if (!origin->suspects) - goto finish; - - for (i = 0, sg = first_scapegoat(revs, commit); - i < num_sg && sg; - sg = sg->next, i++) { - struct origin *porigin = sg_origin[i]; - find_copy_in_parent(sb, &blametail, &toosmall, - origin, sg->item, porigin, opt); - if (!origin->suspects) - goto finish; - } - } - -finish: - *blametail = NULL; - distribute_blame(sb, blames); - /* - * prepend toosmall to origin->suspects - * - * There is no point in sorting: this ends up on a big - * unsorted list in the caller anyway. - */ - if (toosmall) { - struct blame_entry **tail = &toosmall; - while (*tail) - tail = &(*tail)->next; - *tail = origin->suspects; - origin->suspects = toosmall; - } - for (i = 0; i < num_sg; i++) { - if (sg_origin[i]) { - drop_origin_blob(sg_origin[i]); - origin_decref(sg_origin[i]); - } - } - drop_origin_blob(origin); - if (sg_buf != sg_origin) - free(sg_origin); + return blame_nth_line((struct blame_scoreboard *)data, lno); } /* @@@ -78,13 -1561,13 +78,13 @@@ struct commit_info { struct strbuf author; struct strbuf author_mail; - unsigned long author_time; + timestamp_t author_time; struct strbuf author_tz; /* filled only when asked for details */ struct strbuf committer; struct strbuf committer_mail; - unsigned long committer_time; + timestamp_t committer_time; struct strbuf committer_tz; struct strbuf summary; @@@ -95,7 -1578,7 +95,7 @@@ */ static void get_ac_line(const char *inbuf, const char *what, struct strbuf *name, struct strbuf *mail, - unsigned long *time, struct strbuf *tz) + timestamp_t *time, struct strbuf *tz) { struct ident_split ident; size_t len, maillen, namelen; @@@ -216,10 -1699,10 +216,10 @@@ static void get_commit_info(struct comm * To allow LF and other nonportable characters in pathnames, * they are c-style quoted as needed. */ -static void write_filename_info(struct origin *suspect) +static void write_filename_info(struct blame_origin *suspect) { if (suspect->previous) { - struct origin *prev = suspect->previous; + struct blame_origin *prev = suspect->previous; printf("previous %s ", oid_to_hex(&prev->commit->object.oid)); write_name_quoted(prev->path, stdout, '\n'); } @@@ -233,7 -1716,7 +233,7 @@@ * the first time each commit appears in the output (unless the * user has specifically asked for us to repeat). */ -static int emit_one_suspect_detail(struct origin *suspect, int repeat) +static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat) { struct commit_info ci; @@@ -244,11 -1727,11 +244,11 @@@ get_commit_info(suspect->commit, &ci, 1); printf("author %s\n", ci.author.buf); printf("author-mail %s\n", ci.author_mail.buf); - printf("author-time %lu\n", ci.author_time); + printf("author-time %"PRItime"\n", ci.author_time); printf("author-tz %s\n", ci.author_tz.buf); printf("committer %s\n", ci.committer.buf); printf("committer-mail %s\n", ci.committer_mail.buf); - printf("committer-time %lu\n", ci.committer_time); + printf("committer-time %"PRItime"\n", ci.committer_time); printf("committer-tz %s\n", ci.committer_tz.buf); printf("summary %s\n", ci.summary.buf); if (suspect->commit->object.flags & UNINTERESTING) @@@ -263,12 -1746,11 +263,12 @@@ * The blame_entry is found to be guilty for the range. * Show it in incremental output. */ -static void found_guilty_entry(struct blame_entry *ent, - struct progress_info *pi) +static void found_guilty_entry(struct blame_entry *ent, void *data) { + struct progress_info *pi = (struct progress_info *)data; + if (incremental) { - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; printf("%s %d %d %d\n", oid_to_hex(&suspect->commit->object.oid), @@@ -281,14 -1763,88 +281,14 @@@ display_progress(pi->progress, pi->blamed_lines); } -/* - * The main loop -- while we have blobs with lines whose true origin - * is still unknown, pick one blob, and allow its lines to pass blames - * to its parents. */ -static void assign_blame(struct scoreboard *sb, int opt) -{ - struct rev_info *revs = sb->revs; - struct commit *commit = prio_queue_get(&sb->commits); - struct progress_info pi = { NULL, 0 }; - - if (show_progress) - pi.progress = start_progress_delay(_("Blaming lines"), - sb->num_lines, 50, 1); - - while (commit) { - struct blame_entry *ent; - struct origin *suspect = commit->util; - - /* find one suspect to break down */ - while (suspect && !suspect->suspects) - suspect = suspect->next; - - if (!suspect) { - commit = prio_queue_get(&sb->commits); - continue; - } - - assert(commit == suspect->commit); - - /* - * We will use this suspect later in the loop, - * so hold onto it in the meantime. - */ - origin_incref(suspect); - parse_commit(commit); - if (reverse || - (!(commit->object.flags & UNINTERESTING) && - !(revs->max_age != -1 && commit->date < revs->max_age))) - pass_blame(sb, suspect, opt); - else { - commit->object.flags |= UNINTERESTING; - if (commit->object.parsed) - mark_parents_uninteresting(commit); - } - /* treat root commit as boundary */ - if (!commit->parents && !show_root) - commit->object.flags |= UNINTERESTING; - - /* Take responsibility for the remaining entries */ - ent = suspect->suspects; - if (ent) { - suspect->guilty = 1; - for (;;) { - struct blame_entry *next = ent->next; - found_guilty_entry(ent, &pi); - if (next) { - ent = next; - continue; - } - ent->next = sb->ent; - sb->ent = suspect->suspects; - suspect->suspects = NULL; - break; - } - } - origin_decref(suspect); - - if (DEBUG) /* sanity */ - sanity_check_refcnt(sb); - } - - stop_progress(&pi.progress); -} - -static const char *format_time(unsigned long time, const char *tz_str, +static const char *format_time(timestamp_t time, const char *tz_str, int show_raw_time) { static struct strbuf time_buf = STRBUF_INIT; strbuf_reset(&time_buf); if (show_raw_time) { - strbuf_addf(&time_buf, "%lu %s", time, tz_str); + strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str); } else { const char *time_str; @@@ -320,20 -1876,20 +320,20 @@@ #define OUTPUT_SHOW_EMAIL 0400 #define OUTPUT_LINE_PORCELAIN 01000 -static void emit_porcelain_details(struct origin *suspect, int repeat) +static void emit_porcelain_details(struct blame_origin *suspect, int repeat) { if (emit_one_suspect_detail(suspect, repeat) || (suspect->commit->object.flags & MORE_THAN_ONE_PATH)) write_filename_info(suspect); } -static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent, +static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int repeat = opt & OUTPUT_LINE_PORCELAIN; int cnt; const char *cp; - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; char hex[GIT_MAX_HEXSZ + 1]; oid_to_hex_r(hex, &suspect->commit->object.oid); @@@ -344,7 -1900,7 +344,7 @@@ ent->num_lines); emit_porcelain_details(suspect, repeat); - cp = nth_line(sb, ent->lno); + cp = blame_nth_line(sb, ent->lno); for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; if (cnt) { @@@ -366,11 -1922,11 +366,11 @@@ putchar('\n'); } -static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) +static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) { int cnt; const char *cp; - struct origin *suspect = ent->suspect; + struct blame_origin *suspect = ent->suspect; struct commit_info ci; char hex[GIT_MAX_HEXSZ + 1]; int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); @@@ -378,7 -1934,7 +378,7 @@@ get_commit_info(suspect->commit, &ci, 1); oid_to_hex_r(hex, &suspect->commit->object.oid); - cp = nth_line(sb, ent->lno); + cp = blame_nth_line(sb, ent->lno); for (cnt = 0; cnt < ent->num_lines; cnt++) { char ch; int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev; @@@ -445,14 -2001,14 +445,14 @@@ commit_info_destroy(&ci); } -static void output(struct scoreboard *sb, int option) +static void output(struct blame_scoreboard *sb, int option) { struct blame_entry *ent; if (option & OUTPUT_PORCELAIN) { for (ent = sb->ent; ent; ent = ent->next) { int count = 0; - struct origin *suspect; + struct blame_origin *suspect; struct commit *commit = ent->suspect->commit; if (commit->object.flags & MORE_THAN_ONE_PATH) continue; @@@ -474,6 -2030,40 +474,6 @@@ } } -static const char *get_next_line(const char *start, const char *end) -{ - const char *nl = memchr(start, '\n', end - start); - return nl ? nl + 1 : end; -} - -/* - * To allow quick access to the contents of nth line in the - * final image, prepare an index in the scoreboard. - */ -static int prepare_lines(struct scoreboard *sb) -{ - const char *buf = sb->final_buf; - unsigned long len = sb->final_buf_size; - const char *end = buf + len; - const char *p; - int *lineno; - int num = 0; - - for (p = buf; p < end; p = get_next_line(p, end)) - num++; - - ALLOC_ARRAY(sb->lineno, num + 1); - lineno = sb->lineno; - - for (p = buf; p < end; p = get_next_line(p, end)) - *lineno++ = p - buf; - - *lineno = len; - - sb->num_lines = num; - return sb->num_lines; -} - /* * Add phony grafts for use with -S; this is primarily to * support git's cvsserver that wants to give a linear history @@@ -481,7 -2071,7 +481,7 @@@ */ static int read_ancestry(const char *graft_file) { - FILE *fp = fopen(graft_file, "r"); + FILE *fp = fopen_or_warn(graft_file, "r"); struct strbuf buf = STRBUF_INIT; if (!fp) return -1; @@@ -496,7 -2086,7 +496,7 @@@ return 0; } -static int update_auto_abbrev(int auto_abbrev, struct origin *suspect) +static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) { const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash, auto_abbrev); @@@ -510,7 -2100,7 +510,7 @@@ * How many columns do we need to show line numbers, authors, * and filenames? */ -static void find_alignment(struct scoreboard *sb, int *option) +static void find_alignment(struct blame_scoreboard *sb, int *option) { int longest_src_lines = 0; int longest_dst_lines = 0; @@@ -520,7 -2110,7 +520,7 @@@ int auto_abbrev = DEFAULT_ABBREV; for (e = sb->ent; e; e = e->next) { - struct origin *suspect = e->suspect; + struct blame_origin *suspect = e->suspect; int num; if (compute_auto_abbrev) @@@ -548,8 -2138,8 +548,8 @@@ num = e->lno + e->num_lines; if (longest_dst_lines < num) longest_dst_lines = num; - if (largest_score < ent_score(sb, e)) - largest_score = ent_score(sb, e); + if (largest_score < blame_entry_score(sb, e)) + largest_score = blame_entry_score(sb, e); } max_orig_digits = decimal_width(longest_src_lines); max_digits = decimal_width(longest_dst_lines); @@@ -560,12 -2150,31 +560,12 @@@ abbrev = auto_abbrev + 1; } -/* - * For debugging -- origin is refcounted, and this asserts that - * we do not underflow. - */ -static void sanity_check_refcnt(struct scoreboard *sb) +static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa) { - int baa = 0; - struct blame_entry *ent; - - for (ent = sb->ent; ent; ent = ent->next) { - /* Nobody should have zero or negative refcnt */ - if (ent->suspect->refcnt <= 0) { - fprintf(stderr, "%s in %s has negative refcnt %d\n", - ent->suspect->path, - oid_to_hex(&ent->suspect->commit->object.oid), - ent->suspect->refcnt); - baa = 1; - } - } - if (baa) { - int opt = 0160; - find_alignment(sb, &opt); - output(sb, opt); - die("Baa %d!", baa); - } + int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME; + find_alignment(sb, &opt); + output(sb, opt); + die("Baa %d!", baa); } static unsigned parse_score(const char *arg) @@@ -615,6 -2224,301 +615,6 @@@ static int git_blame_config(const char return git_default_config(var, value, cb); } -static void verify_working_tree_path(struct commit *work_tree, const char *path) -{ - struct commit_list *parents; - int pos; - - for (parents = work_tree->parents; parents; parents = parents->next) { - const struct object_id *commit_oid = &parents->item->object.oid; - struct object_id blob_oid; - unsigned mode; - - if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) && - sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) - return; - } - - pos = cache_name_pos(path, strlen(path)); - if (pos >= 0) - ; /* path is in the index */ - else if (-1 - pos < active_nr && - !strcmp(active_cache[-1 - pos]->name, path)) - ; /* path is in the index, unmerged */ - else - die("no such path '%s' in HEAD", path); -} - -static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid) -{ - struct commit *parent; - - parent = lookup_commit_reference(oid->hash); - if (!parent) - die("no such commit %s", oid_to_hex(oid)); - return &commit_list_insert(parent, tail)->next; -} - -static void append_merge_parents(struct commit_list **tail) -{ - int merge_head; - struct strbuf line = STRBUF_INIT; - - merge_head = open(git_path_merge_head(), O_RDONLY); - if (merge_head < 0) { - if (errno == ENOENT) - return; - die("cannot open '%s' for reading", git_path_merge_head()); - } - - while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) { - struct object_id oid; - if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid)) - die("unknown line in '%s': %s", git_path_merge_head(), line.buf); - tail = append_parent(tail, &oid); - } - close(merge_head); - strbuf_release(&line); -} - -/* - * This isn't as simple as passing sb->buf and sb->len, because we - * want to transfer ownership of the buffer to the commit (so we - * must use detach). - */ -static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb) -{ - size_t len; - void *buf = strbuf_detach(sb, &len); - set_commit_buffer(c, buf, len); -} - -/* - * Prepare a dummy commit that represents the work tree (or staged) item. - * Note that annotating work tree item never works in the reverse. - */ -static struct commit *fake_working_tree_commit(struct diff_options *opt, - const char *path, - const char *contents_from) -{ - struct commit *commit; - struct origin *origin; - struct commit_list **parent_tail, *parent; - struct object_id head_oid; - struct strbuf buf = STRBUF_INIT; - const char *ident; - time_t now; - int size, len; - struct cache_entry *ce; - unsigned mode; - struct strbuf msg = STRBUF_INIT; - - read_cache(); - time(&now); - commit = alloc_commit_node(); - commit->object.parsed = 1; - commit->date = now; - parent_tail = &commit->parents; - - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) - die("no such ref: HEAD"); - - parent_tail = append_parent(parent_tail, &head_oid); - append_merge_parents(parent_tail); - verify_working_tree_path(commit, path); - - origin = make_origin(commit, path); - - ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0); - strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n"); - for (parent = commit->parents; parent; parent = parent->next) - strbuf_addf(&msg, "parent %s\n", - oid_to_hex(&parent->item->object.oid)); - strbuf_addf(&msg, - "author %s\n" - "committer %s\n\n" - "Version of %s from %s\n", - ident, ident, path, - (!contents_from ? path : - (!strcmp(contents_from, "-") ? "standard input" : contents_from))); - set_commit_buffer_from_strbuf(commit, &msg); - - if (!contents_from || strcmp("-", contents_from)) { - struct stat st; - const char *read_from; - char *buf_ptr; - unsigned long buf_len; - - if (contents_from) { - if (stat(contents_from, &st) < 0) - die_errno("Cannot stat '%s'", contents_from); - read_from = contents_from; - } - else { - if (lstat(path, &st) < 0) - die_errno("Cannot lstat '%s'", path); - read_from = path; - } - mode = canon_mode(st.st_mode); - - switch (st.st_mode & S_IFMT) { - case S_IFREG: - if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && - textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len)) - strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1); - else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) - die_errno("cannot open or read '%s'", read_from); - break; - case S_IFLNK: - if (strbuf_readlink(&buf, read_from, st.st_size) < 0) - die_errno("cannot readlink '%s'", read_from); - break; - default: - die("unsupported file type %s", read_from); - } - } - else { - /* Reading from stdin */ - mode = 0; - if (strbuf_read(&buf, 0, 0) < 0) - die_errno("failed to read from stdin"); - } - convert_to_git(path, buf.buf, buf.len, &buf, 0); - origin->file.ptr = buf.buf; - origin->file.size = buf.len; - pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash); - - /* - * Read the current index, replace the path entry with - * origin->blob_sha1 without mucking with its mode or type - * bits; we are not going to write this index out -- we just - * want to run "diff-index --cached". - */ - discard_cache(); - read_cache(); - - len = strlen(path); - if (!mode) { - int pos = cache_name_pos(path, len); - if (0 <= pos) - mode = active_cache[pos]->ce_mode; - else - /* Let's not bother reading from HEAD tree */ - mode = S_IFREG | 0644; - } - size = cache_entry_size(len); - ce = xcalloc(1, size); - oidcpy(&ce->oid, &origin->blob_oid); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(0); - ce->ce_namelen = len; - ce->ce_mode = create_ce_mode(mode); - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); - - cache_tree_invalidate_path(&the_index, path); - - return commit; -} - -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 = name; - return found; -} - -static char *prepare_final(struct scoreboard *sb) -{ - const char *name; - sb->final = find_single_final(sb->revs, &name); - return xstrdup_or_null(name); -} - -static const char *dwim_reverse_initial(struct scoreboard *sb) -{ - /* - * 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; - unsigned char head_sha1[20]; - - if (sb->revs->pending.nr != 1) - return NULL; - - /* Is that sole rev a committish? */ - obj = sb->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_sha1, NULL)) - return NULL; - head_commit = lookup_commit_reference_gently(head_sha1, 1); - if (!head_commit) - return NULL; - - /* Turn "ONE" into "ONE..HEAD" then */ - obj->flags |= UNINTERESTING; - add_pending_object(sb->revs, &head_commit->object, "HEAD"); - - sb->final = (struct commit *)obj; - return sb->revs->pending.objects[0].name; -} - -static char *prepare_initial(struct scoreboard *sb) -{ - int i; - const char *final_commit_name = NULL; - struct rev_info *revs = sb->revs; - - /* - * There must be one and only one negative commit, and it must be - * the boundary. - */ - 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 (sb->final) - die("More than one commit to dig up from, %s and %s?", - revs->pending.objects[i].name, - final_commit_name); - sb->final = (struct commit *) obj; - final_commit_name = revs->pending.objects[i].name; - } - - if (!final_commit_name) - final_commit_name = dwim_reverse_initial(sb); - if (!final_commit_name) - die("No commit to dig up from?"); - return xstrdup(final_commit_name); -} - static int blame_copy_callback(const struct option *option, const char *arg, int unset) { int *opt = option->value; @@@ -652,11 -2556,13 +652,11 @@@ int cmd_blame(int argc, const char **ar { struct rev_info revs; const char *path; - struct scoreboard sb; - struct origin *o; + struct blame_scoreboard sb; + struct blame_origin *o; struct blame_entry *ent = NULL; long dashdash_pos, lno; - char *final_commit_name = NULL; - enum object_type type; - struct commit *final_commit = NULL; + struct progress_info pi = { NULL, 0 }; struct string_list range_list = STRING_LIST_INIT_NODUP; int output_option = 0, opt = 0; @@@ -782,15 -2688,12 +782,15 @@@ parse_done blame_date_width = sizeof("2006-10-19"); break; case DATE_RELATIVE: - /* TRANSLATORS: This string is used to tell us the maximum - display width for a relative timestamp in "git blame" - output. For C locale, "4 years, 11 months ago", which - takes 22 places, is the longest among various forms of - relative timestamps, but your language may need more or - fewer display columns. */ + /* + * TRANSLATORS: This string is used to tell us the + * maximum display width for a relative timestamp in + * "git blame" output. For C locale, "4 years, 11 + * months ago", which takes 22 places, is the longest + * among various forms of relative timestamps, but + * your language may need more or fewer display + * columns. + */ blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ break; case DATE_NORMAL: @@@ -806,6 -2709,11 +806,6 @@@ opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | PICKAXE_BLAME_COPY_HARDER); - if (!blame_move_score) - blame_move_score = BLAME_DEFAULT_MOVE_SCORE; - if (!blame_copy_score) - blame_copy_score = BLAME_DEFAULT_COPY_SCORE; - /* * We have collected options unknown to us in argv[1..unk] * which are to be passed to revision machinery if we are @@@ -858,13 -2766,94 +858,13 @@@ revs.disable_stdin = 1; setup_revisions(argc, argv, &revs, NULL); - memset(&sb, 0, sizeof(sb)); + init_scoreboard(&sb); sb.revs = &revs; - if (!reverse) { - final_commit_name = prepare_final(&sb); - sb.commits.compare = compare_commits_by_commit_date; - } - else if (contents_from) - die(_("--contents and --reverse do not blend well.")); - else { - final_commit_name = prepare_initial(&sb); - sb.commits.compare = compare_commits_by_reverse_commit_date; - if (revs.first_parent_only) - revs.children.name = NULL; - } - - if (!sb.final) { - /* - * "--not A B -- path" without anything positive; - * do not default to HEAD, but use the working tree - * or "--contents". - */ - setup_work_tree(); - sb.final = fake_working_tree_commit(&sb.revs->diffopt, - path, contents_from); - add_pending_object(&revs, &(sb.final->object), ":"); - } - else if (contents_from) - die(_("cannot use --contents with final commit object name")); - - if (reverse && revs.first_parent_only) { - final_commit = find_single_final(sb.revs, NULL); - if (!final_commit) - die(_("--reverse and --first-parent together require specified latest commit")); - } - - /* - * If we have bottom, this will mark the ancestors of the - * bottom commits we would reach while traversing as - * uninteresting. - */ - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - - if (reverse && revs.first_parent_only) { - struct commit *c = final_commit; - - sb.revs->children.name = "children"; - while (c->parents && - oidcmp(&c->object.oid, &sb.final->object.oid)) { - struct commit_list *l = xcalloc(1, sizeof(*l)); - - l->item = c; - if (add_decoration(&sb.revs->children, - &c->parents->item->object, l)) - die("BUG: not unique item in first-parent chain"); - c = c->parents->item; - } - - if (oidcmp(&c->object.oid, &sb.final->object.oid)) - die(_("--reverse --first-parent together require range along first-parent chain")); - } - - if (is_null_oid(&sb.final->object.oid)) { - o = sb.final->util; - sb.final_buf = xmemdupz(o->file.ptr, o->file.size); - sb.final_buf_size = o->file.size; - } - else { - o = get_origin(&sb, sb.final, path); - if (fill_blob_sha1_and_mode(o)) - die(_("no such path %s in %s"), path, final_commit_name); - - if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) && - textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf, - &sb.final_buf_size)) - ; - else - sb.final_buf = read_sha1_file(o->blob_oid.hash, &type, - &sb.final_buf_size); - - if (!sb.final_buf) - die(_("cannot read blob %s for path %s"), - oid_to_hex(&o->blob_oid), - path); - } - num_read_blob++; - lno = prepare_lines(&sb); + sb.contents_from = contents_from; + sb.reverse = reverse; + setup_scoreboard(&sb, path, &o); + lno = sb.num_lines; if (lno && !range_list.nr) string_list_append(&range_list, "1"); @@@ -893,13 -2882,22 +893,13 @@@ for (range_i = ranges.nr; range_i > 0; --range_i) { const struct range *r = &ranges.ranges[range_i - 1]; - long bottom = r->start; - long top = r->end; - struct blame_entry *next = ent; - ent = xcalloc(1, sizeof(*ent)); - ent->lno = bottom; - ent->num_lines = top - bottom; - ent->suspect = o; - ent->s_lno = bottom; - ent->next = next; - origin_incref(o); + ent = blame_entry_prepend(ent, r->start, r->end, o); } o->suspects = ent; prio_queue_put(&sb.commits, o->commit); - origin_decref(o); + blame_origin_decref(o); range_set_release(&ranges); string_list_clear(&range_list, 0); @@@ -907,38 -2905,21 +907,38 @@@ sb.ent = NULL; sb.path = path; + if (blame_move_score) + sb.move_score = blame_move_score; + if (blame_copy_score) + sb.copy_score = blame_copy_score; + + sb.debug = DEBUG; + sb.on_sanity_fail = &sanity_check_on_fail; + + sb.show_root = show_root; + sb.xdl_opts = xdl_opts; + sb.no_whole_file_rename = no_whole_file_rename; + read_mailmap(&mailmap, NULL); + sb.found_guilty_entry = &found_guilty_entry; + sb.found_guilty_entry_data = π + if (show_progress) + pi.progress = start_progress_delay(_("Blaming lines"), + sb.num_lines, 50, 1); + assign_blame(&sb, opt); + stop_progress(&pi.progress); + if (!incremental) setup_pager(); - - free(final_commit_name); - - if (incremental) + else return 0; - sb.ent = blame_sort(sb.ent, compare_blame_final); + blame_sort_final(&sb); - coalesce(&sb); + blame_coalesce(&sb); if (!(output_option & OUTPUT_PORCELAIN)) find_alignment(&sb, &output_option); @@@ -952,9 -2933,9 +952,9 @@@ } if (show_stats) { - printf("num read blob: %d\n", num_read_blob); - printf("num get patch: %d\n", num_get_patch); - printf("num commits: %d\n", num_commits); + printf("num read blob: %d\n", sb.num_read_blob); + printf("num get patch: %d\n", sb.num_get_patch); + printf("num commits: %d\n", sb.num_commits); } return 0; } diff --combined builtin/clone.c index 743f16ae2a,dde4fe73af..a2ea019c59 --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -40,7 -40,6 +40,7 @@@ static const char * const builtin_clone static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared; +static int option_no_tags; static int option_shallow_submodules; static int deepen; static char *option_template, *option_depth, *option_since; @@@ -121,8 -120,6 +121,8 @@@ static struct option builtin_clone_opti N_("deepen history of shallow clone, excluding rev")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), + OPT_BOOL(0, "no-tags", &option_no_tags, + N_("don't clone any tags, and make later fetches not to follow them")), OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, N_("any cloned submodules will be shallow")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), @@@ -360,7 -357,7 +360,7 @@@ static void copy_alternates(struct strb * to turn entries with paths relative to the original * absolute, so that they can be used in the new repository. */ - FILE *in = fopen(src->buf, "r"); + FILE *in = xfopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in) != EOF) { @@@ -566,7 -563,7 +566,7 @@@ static struct ref *wanted_peer_refs(con } else get_fetch_map(refs, refspec, &tail, 0); - if (!option_mirror && !option_single_branch) + if (!option_mirror && !option_single_branch && !option_no_tags) get_fetch_map(refs, tag_refspec, &tail, 0); return local_refs; @@@ -655,7 -652,7 +655,7 @@@ static void update_remote_refs(const st if (refs) { write_remote_refs(mapped_refs); - if (option_single_branch) + if (option_single_branch && !option_no_tags) write_followtags(refs, msg); } @@@ -685,7 -682,7 +685,7 @@@ static void update_head(const struct re install_branch_config(0, head, option_origin, our->name); } } else if (our) { - struct commit *c = lookup_commit_reference(our->old_oid.hash); + struct commit *c = lookup_commit_reference(&our->old_oid); /* --branch specifies a non-branch (i.e. tags), detach HEAD */ update_ref(msg, "HEAD", c->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); @@@ -742,7 -739,7 +742,7 @@@ static int checkout(int submodule_progr opts.src_index = &the_index; opts.dst_index = &the_index; - tree = parse_tree_indirect(oid.hash); + tree = parse_tree_indirect(&oid); parse_tree(tree); init_tree_desc(&t, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) @@@ -776,9 -773,7 +776,9 @@@ static int write_one_config(const char *key, const char *value, void *data) { - return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0); + return git_config_set_multivar_gently(key, + value ? value : "true", + CONFIG_REGEX_NONE, 0); } static void write_config(struct string_list *config) @@@ -1040,12 -1035,6 +1040,12 @@@ int cmd_clone(int argc, const char **ar git_config_set(key.buf, repo); strbuf_reset(&key); + if (option_no_tags) { + strbuf_addf(&key, "remote.%s.tagOpt", option_origin); + git_config_set(key.buf, "--no-tags"); + strbuf_reset(&key); + } + if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); diff --combined builtin/commit.c index da1ba4c862,eda0d32311..ef52457eff --- a/builtin/commit.c +++ b/builtin/commit.c @@@ -313,7 -313,7 +313,7 @@@ static void create_base_index(const str opts.dst_index = &the_index; opts.fn = oneway_merge; - tree = parse_tree_indirect(current_head->object.oid.hash); + tree = parse_tree_indirect(¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); parse_tree(tree); @@@ -1263,10 -1263,6 +1263,10 @@@ static int parse_status_slot(const cha return WT_STATUS_NOBRANCH; if (!strcasecmp(slot, "unmerged")) return WT_STATUS_UNMERGED; + if (!strcasecmp(slot, "localBranch")) + return WT_STATUS_LOCAL_BRANCH; + if (!strcasecmp(slot, "remoteBranch")) + return WT_STATUS_REMOTE_BRANCH; return -1; } @@@ -1434,7 -1430,7 +1434,7 @@@ static void print_summary(const char *p struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; - commit = lookup_commit(oid->hash); + commit = lookup_commit(oid); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) @@@ -1658,7 -1654,7 +1658,7 @@@ int cmd_commit(int argc, const char **a if (get_sha1("HEAD", oid.hash)) current_head = NULL; else { - current_head = lookup_commit_or_die(oid.hash, "HEAD"); + current_head = lookup_commit_or_die(&oid, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } @@@ -1699,10 -1695,7 +1699,7 @@@ if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = commit_list_append(current_head, pptr); - fp = fopen(git_path_merge_head(), "r"); - if (fp == NULL) - die_errno(_("could not open '%s' for reading"), - git_path_merge_head()); + fp = xfopen(git_path_merge_head(), "r"); while (strbuf_getline_lf(&m, fp) != EOF) { struct commit *parent; @@@ -1739,7 -1732,7 +1736,7 @@@ if (verbose || /* Truncate the message just before the diff, if any. */ cleanup_mode == CLEANUP_SCISSORS) - wt_status_truncate_message_at_cut_line(&sb); + strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len)); if (cleanup_mode != CLEANUP_NONE) strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL); @@@ -1762,7 -1755,7 +1759,7 @@@ append_merge_tag_headers(parents, &tail); } - if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1, + if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->oid.hash, parents, oid.hash, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); diff --combined builtin/fast-export.c index 24e29ad7ea,128b99e6da..2dfed87454 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@@ -232,7 -232,7 +232,7 @@@ static void export_blob(const struct ob if (anonymize) { buf = anonymize_blob(&size); - object = (struct object *)lookup_blob(oid->hash); + object = (struct object *)lookup_blob(oid); eaten = 0; } else { buf = read_sha1_file(oid->hash, &type, &size); @@@ -240,7 -240,7 +240,7 @@@ die ("Could not read blob %s", oid_to_hex(oid)); if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0) die("sha1 mismatch in blob %s", oid_to_hex(oid)); - object = parse_object_buffer(oid->hash, type, size, buf, &eaten); + object = parse_object_buffer(oid, type, size, buf, &eaten); } if (!object) @@@ -734,7 -734,6 +734,7 @@@ static void handle_tag(const char *name oid_to_hex(&tag->object.oid)); case DROP: /* Ignore this tag altogether */ + free(buf); return; case REWRITE: if (tagged->type != OBJ_COMMIT) { @@@ -766,7 -765,6 +766,7 @@@ (int)(tagger_end - tagger), tagger, tagger == tagger_end ? "" : "\n", (int)message_size, (int)message_size, message ? message : ""); + free(buf); } static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name) @@@ -779,7 -777,7 +779,7 @@@ /* handle nested tags */ while (tag && tag->object.type == OBJ_TAG) { - parse_object(tag->object.oid.hash); + parse_object(&tag->object.oid); string_list_append(&extra_refs, full_name)->util = tag; tag = (struct tag *)tag->tagged; } @@@ -907,9 -905,7 +907,7 @@@ static void export_marks(char *file static void import_marks(char *input_file) { char line[512]; - FILE *f = fopen(input_file, "r"); - if (!f) - die_errno("cannot read '%s'", input_file); + FILE *f = xfopen(input_file, "r"); while (fgets(line, sizeof(line), f)) { uint32_t mark; @@@ -940,7 -936,7 +938,7 @@@ /* only commits */ continue; - commit = lookup_commit(oid.hash); + commit = lookup_commit(&oid); if (!commit) die("not a commit? can't happen: %s", oid_to_hex(&oid)); diff --combined builtin/fsck.c index cb2ba6cd1b,00beaaa4e6..3a2c27f241 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@@ -280,8 -280,7 +280,7 @@@ static void check_unreachable_object(st free(filename); return; } - if (!(f = fopen(filename, "w"))) - die_errno("Could not open '%s'", filename); + f = xfopen(filename, "w"); if (obj->type == OBJ_BLOB) { if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1)) die_errno("Could not write '%s'", filename); @@@ -377,7 -376,7 +376,7 @@@ static int fsck_obj(struct object *obj return 0; } -static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, +static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten) { /* @@@ -385,10 -384,10 +384,10 @@@ * verify_packfile(), data_valid variable for details. */ struct object *obj; - obj = parse_object_buffer(sha1, type, size, buffer, eaten); + obj = parse_object_buffer(oid, type, size, buffer, eaten); if (!obj) { errors_found |= ERROR_OBJECT; - return error("%s: object corrupt or missing", sha1_to_hex(sha1)); + return error("%s: object corrupt or missing", oid_to_hex(oid)); } obj->flags = HAS_OBJ; return fsck_obj(obj); @@@ -397,7 -396,7 +396,7 @@@ static int default_refs; static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - unsigned long timestamp) + timestamp_t timestamp) { struct object *obj; @@@ -407,7 -406,7 +406,7 @@@ if (timestamp && name_objects) add_decoration(fsck_walk_options.object_names, obj, - xstrfmt("%s@{%ld}", refname, timestamp)); + xstrfmt("%s@{%"PRItime"}", refname, timestamp)); obj->used = 1; mark_object_reachable(obj); } else { @@@ -418,7 -417,7 +417,7 @@@ } static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { const char *refname = cb_data; @@@ -444,7 -443,7 +443,7 @@@ static int fsck_handle_ref(const char * { struct object *obj; - obj = parse_object(oid->hash); + obj = parse_object(oid); if (!obj) { error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; @@@ -506,7 -505,7 +505,7 @@@ static struct object *parse_loose_objec if (!contents && type != OBJ_BLOB) die("BUG: read_loose_object streamed a non-blob"); - obj = parse_object_buffer(oid->hash, type, size, contents, &eaten); + obj = parse_object_buffer(oid, type, size, contents, &eaten); if (!eaten) free(contents); @@@ -599,10 -598,10 +598,10 @@@ static int fsck_cache_tree(struct cache fprintf(stderr, "Checking cache tree\n"); if (0 <= it->entry_count) { - struct object *obj = parse_object(it->sha1); + struct object *obj = parse_object(&it->oid); if (!obj) { error("%s: invalid sha1 pointer in cache-tree", - sha1_to_hex(it->sha1)); + oid_to_hex(&it->oid)); errors_found |= ERROR_REFS; return 1; } @@@ -781,7 -780,7 +780,7 @@@ int cmd_fsck(int argc, const char **arg mode = active_cache[i]->ce_mode; if (S_ISGITLINK(mode)) continue; - blob = lookup_blob(active_cache[i]->oid.hash); + blob = lookup_blob(&active_cache[i]->oid); if (!blob) continue; obj = &blob->object; diff --combined builtin/log.c index e89ec941ce,f075838df9..a33c1a70ab --- a/builtin/log.c +++ b/builtin/log.c @@@ -110,8 -110,6 +110,8 @@@ static void init_log_defaults(void { init_grep_defaults(); init_diff_ui_defaults(); + + decoration_style = auto_decoration_style(); } static void cmd_log_init_defaults(struct rev_info *rev) @@@ -412,6 -410,8 +412,6 @@@ static int git_log_config(const char *v if (decoration_style < 0) decoration_style = 0; /* maybe warn? */ return 0; - } else { - decoration_style = auto_decoration_style(); } if (!strcmp(var, "log.showroot")) { default_show_root = git_config_bool(var, value); @@@ -483,20 -483,16 +483,20 @@@ static int show_blob_object(const struc !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV)) return stream_blob_to_fd(1, oid, NULL, 0); - if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context)) + if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH, + oidc.hash, &obj_context)) die(_("Not a valid object name %s"), obj_name); - if (!obj_context.path[0] || - !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) + if (!obj_context.path || + !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) { + free(obj_context.path); return stream_blob_to_fd(1, oid, NULL, 0); + } if (!buf) die(_("git show %s: bad file"), obj_name); write_or_die(1, buf, size); + free(obj_context.path); return 0; } @@@ -600,7 -596,7 +600,7 @@@ int cmd_show(int argc, const char **arg rev.shown_one = 1; if (ret) break; - o = parse_object(t->tagged->oid.hash); + o = parse_object(&t->tagged->oid); if (!o) ret = error(_("Could not read object %s"), oid_to_hex(&t->tagged->oid)); @@@ -846,8 -842,10 +846,10 @@@ static int open_next_file(struct commi if (output_directory) { strbuf_addstr(&filename, output_directory); if (filename.len >= - PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) + PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) { + strbuf_release(&filename); return error(_("name of output directory is too long")); + } strbuf_complete(&filename, '/'); } @@@ -861,8 -859,11 +863,11 @@@ if (!quiet) printf("%s\n", filename.buf + outdir_offset); - if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) - return error(_("Cannot open patch file %s"), filename.buf); + if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) { + error_errno(_("Cannot open patch file %s"), filename.buf); + strbuf_release(&filename); + return -1; + } strbuf_release(&filename); return 0; @@@ -882,8 -883,8 +887,8 @@@ static void get_patch_ids(struct rev_in o2 = rev->pending.objects[1].item; flags1 = o1->flags; flags2 = o2->flags; - c1 = lookup_commit_reference(o1->oid.hash); - c2 = lookup_commit_reference(o2->oid.hash); + c1 = lookup_commit_reference(&o1->oid); + c2 = lookup_commit_reference(&o2->oid); if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) die(_("Not a range.")); @@@ -914,8 -915,8 +919,8 @@@ static void gen_message_id(struct rev_info *info, char *base) { struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "%s.%lu.git.%s", base, - (unsigned long) time(NULL), + strbuf_addf(&buf, "%s.%"PRItime".git.%s", base, + (timestamp_t) time(NULL), git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); info->message_id = strbuf_detach(&buf, NULL); } @@@ -1267,7 -1268,7 +1272,7 @@@ static struct commit *get_base_commit(c if (get_oid(upstream, &oid)) die(_("Failed to resolve '%s' as a valid ref."), upstream); - commit = lookup_commit_or_die(oid.hash, "upstream base"); + commit = lookup_commit_or_die(&oid, "upstream base"); base_list = get_merge_bases_many(commit, total, list); /* There should be one and only one merge base. */ if (!base_list || base_list->next) @@@ -1823,7 -1824,7 +1828,7 @@@ static int add_pending_commit(const cha { struct object_id oid; if (get_oid(arg, &oid) == 0) { - struct commit *commit = lookup_commit_reference(oid.hash); + struct commit *commit = lookup_commit_reference(&oid); if (commit) { commit->object.flags |= flags; add_pending_object(revs, &commit->object, arg); diff --combined builtin/merge.c index a4a098f40f,65a1501858..eab03a026d --- a/builtin/merge.c +++ b/builtin/merge.c @@@ -605,13 -605,13 +605,13 @@@ static int read_tree_trivial(struct obj opts.verbose_update = 1; opts.trivial_merges_only = 1; opts.merge = 1; - trees[nr_trees] = parse_tree_indirect(common->hash); + trees[nr_trees] = parse_tree_indirect(common); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(head->hash); + trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) return -1; - trees[nr_trees] = parse_tree_indirect(one->hash); + trees[nr_trees] = parse_tree_indirect(one); if (!trees[nr_trees++]) return -1; opts.fn = threeway_merge; @@@ -839,9 -839,7 +839,7 @@@ static int suggest_conflicts(void struct strbuf msgbuf = STRBUF_INIT; filename = git_path_merge_msg(); - fp = fopen(filename, "a"); - if (!fp) - die_errno(_("Could not open '%s' for writing"), filename); + fp = xfopen(filename, "a"); append_conflicts_hint(&msgbuf); fputs(msgbuf.buf, fp); @@@ -1123,7 -1121,7 +1121,7 @@@ int cmd_merge(int argc, const char **ar if (!branch || is_null_oid(&head_oid)) head_commit = NULL; else - head_commit = lookup_commit_or_die(head_oid.hash, "HEAD"); + head_commit = lookup_commit_or_die(&head_oid, "HEAD"); init_diff_ui_defaults(); git_config(git_merge_config, NULL); @@@ -1372,8 -1370,8 +1370,8 @@@ goto done; } - if (checkout_fast_forward(head_commit->object.oid.hash, - commit->object.oid.hash, + if (checkout_fast_forward(&head_commit->object.oid, + &commit->object.oid, overwrite_ignore)) { ret = 1; goto done; diff --combined builtin/pull.c index da8b60fc85,589c25becf..69417e4f36 --- a/builtin/pull.c +++ b/builtin/pull.c @@@ -337,8 -337,7 +337,7 @@@ static void get_merge_heads(struct oid_ struct strbuf sb = STRBUF_INIT; struct object_id oid; - if (!(fp = fopen(filename, "r"))) - die_errno(_("could not open '%s' for reading"), filename); + fp = xfopen(filename, "r"); while (strbuf_getline_lf(&sb, fp) != EOF) { if (get_oid_hex(sb.buf, &oid)) continue; /* invalid line: does not start with SHA1 */ @@@ -523,7 -522,7 +522,7 @@@ static int pull_into_void(const struct * index/worktree changes that the user already made on the unborn * branch. */ - if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head->hash, 0)) + if (checkout_fast_forward(&empty_tree_oid, merge_head, 0)) return 1; if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR)) @@@ -698,10 -697,10 +697,10 @@@ static int get_octopus_merge_base(struc { struct commit_list *revs = NULL, *result; - commit_list_insert(lookup_commit_reference(curr_head->hash), &revs); - commit_list_insert(lookup_commit_reference(merge_head->hash), &revs); + commit_list_insert(lookup_commit_reference(curr_head), &revs); + commit_list_insert(lookup_commit_reference(merge_head), &revs); if (!is_null_oid(fork_point)) - commit_list_insert(lookup_commit_reference(fork_point->hash), &revs); + commit_list_insert(lookup_commit_reference(fork_point), &revs); result = reduce_heads(get_octopus_merge_bases(revs)); free_commit_list(revs); @@@ -772,7 -771,6 +771,7 @@@ int cmd_pull(int argc, const char **arg struct oid_array merge_heads = OID_ARRAY_INIT; struct object_id orig_head, curr_head; struct object_id rebase_fork_point; + int autostash; if (!getenv("GIT_REFLOG_ACTION")) set_reflog_message(argc, argv); @@@ -801,8 -799,8 +800,8 @@@ if (!opt_rebase && opt_autostash != -1) die(_("--[no-]autostash option is only valid with --rebase.")); + autostash = config_autostash; if (opt_rebase) { - int autostash = config_autostash; if (opt_autostash != -1) autostash = opt_autostash; @@@ -840,7 -838,7 +839,7 @@@ "fast-forwarding your working tree from\n" "commit %s."), oid_to_hex(&orig_head)); - if (checkout_fast_forward(orig_head.hash, curr_head.hash, 0)) + if (checkout_fast_forward(&orig_head, &curr_head, 0)) die(_("Cannot fast-forward your working tree.\n" "After making sure that you saved anything precious from\n" "$ git diff %s\n" @@@ -863,18 -861,16 +862,18 @@@ die(_("Cannot rebase onto multiple branches.")); if (opt_rebase) { - struct commit_list *list = NULL; - struct commit *merge_head, *head; - - head = lookup_commit_reference(orig_head.hash); - commit_list_insert(head, &list); - merge_head = lookup_commit_reference(merge_heads.oid[0].hash); - if (is_descendant_of(merge_head, list)) { - /* we can fast-forward this without invoking rebase */ - opt_ff = "--ff-only"; - return run_merge(); + if (!autostash) { + struct commit_list *list = NULL; + struct commit *merge_head, *head; + + head = lookup_commit_reference(&orig_head); + commit_list_insert(head, &list); + merge_head = lookup_commit_reference(&merge_heads.oid[0]); + if (is_descendant_of(merge_head, list)) { + /* we can fast-forward this without invoking rebase */ + opt_ff = "--ff-only"; + return run_merge(); + } } return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point); } else { diff --combined commit.c index 713f09feb0,3eeda081f9..99846d9bf4 --- a/commit.c +++ b/commit.c @@@ -11,7 -11,6 +11,7 @@@ #include "commit-slab.h" #include "prio-queue.h" #include "sha1-lookup.h" +#include "wt-status.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@@ -19,38 -18,38 +19,38 @@@ int save_commit_buffer = 1 const char *commit_type = "commit"; -struct commit *lookup_commit_reference_gently(const unsigned char *sha1, +struct commit *lookup_commit_reference_gently(const struct object_id *oid, int quiet) { - struct object *obj = deref_tag(parse_object(sha1), NULL, 0); + struct object *obj = deref_tag(parse_object(oid), NULL, 0); if (!obj) return NULL; return object_as_type(obj, OBJ_COMMIT, quiet); } -struct commit *lookup_commit_reference(const unsigned char *sha1) +struct commit *lookup_commit_reference(const struct object_id *oid) { - return lookup_commit_reference_gently(sha1, 0); + return lookup_commit_reference_gently(oid, 0); } -struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name) +struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name) { - struct commit *c = lookup_commit_reference(sha1); + struct commit *c = lookup_commit_reference(oid); if (!c) die(_("could not parse %s"), ref_name); - if (hashcmp(sha1, c->object.oid.hash)) { + if (oidcmp(oid, &c->object.oid)) { warning(_("%s %s is not a commit!"), - ref_name, sha1_to_hex(sha1)); + ref_name, oid_to_hex(oid)); } return c; } -struct commit *lookup_commit(const unsigned char *sha1) +struct commit *lookup_commit(const struct object_id *oid) { - struct object *obj = lookup_object(sha1); + struct object *obj = lookup_object(oid->hash); if (!obj) - return create_object(sha1, alloc_commit_node()); + return create_object(oid->hash, alloc_commit_node()); return object_as_type(obj, OBJ_COMMIT, 0); } @@@ -61,13 -60,13 +61,13 @@@ struct commit *lookup_commit_reference_ if (get_sha1_committish(name, oid.hash)) return NULL; - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (parse_commit(commit)) return NULL; return commit; } -static unsigned long parse_commit_date(const char *buf, const char *tail) +static timestamp_t parse_commit_date(const char *buf, const char *tail) { const char *dateptr; @@@ -90,8 -89,8 +90,8 @@@ /* nada */; if (buf >= tail) return 0; - /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ - return strtoul(dateptr, NULL, 10); + /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */ + return parse_timestamp(dateptr, NULL, 10); } static struct commit_graft **commit_graft; @@@ -168,7 -167,7 +168,7 @@@ bad_graft_data static int read_graft_file(const char *graft_file) { - FILE *fp = fopen(graft_file, "r"); + FILE *fp = fopen_or_warn(graft_file, "r"); struct strbuf buf = STRBUF_INIT; if (!fp) return -1; @@@ -217,9 -216,9 +217,9 @@@ int for_each_commit_graft(each_commit_g return ret; } -int unregister_shallow(const unsigned char *sha1) +int unregister_shallow(const struct object_id *oid) { - int pos = commit_graft_pos(sha1); + int pos = commit_graft_pos(oid->hash); if (pos < 0) return -1; if (pos + 1 < commit_graft_nr) @@@ -332,7 -331,7 +332,7 @@@ int parse_commit_buffer(struct commit * if (get_sha1_hex(bufptr + 5, parent.hash) < 0) return error("bad tree pointer in commit %s", oid_to_hex(&item->object.oid)); - item->tree = lookup_tree(parent.hash); + item->tree = lookup_tree(&parent); bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; @@@ -351,7 -350,7 +351,7 @@@ */ if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) continue; - new_parent = lookup_commit(parent.hash); + new_parent = lookup_commit(&parent); if (new_parent) pptr = &commit_list_insert(new_parent, pptr)->next; } @@@ -359,7 -358,7 +359,7 @@@ int i; struct commit *new_parent; for (i = 0; i < graft->nr_parent; i++) { - new_parent = lookup_commit(graft->parent[i].hash); + new_parent = lookup_commit(&graft->parent[i]); if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; @@@ -474,8 -473,8 +474,8 @@@ struct commit_list * commit_list_insert static int commit_list_compare_by_date(const void *a, const void *b) { - unsigned long a_date = ((const struct commit_list *)a)->item->date; - unsigned long b_date = ((const struct commit_list *)b)->item->date; + timestamp_t a_date = ((const struct commit_list *)a)->item->date; + timestamp_t b_date = ((const struct commit_list *)b)->item->date; if (a_date < b_date) return 1; if (a_date > b_date) @@@ -563,7 -562,7 +563,7 @@@ void clear_commit_marks_for_object_arra for (i = 0; i < a->nr; i++) { object = a->objects[i].item; - commit = lookup_commit_reference_gently(object->oid.hash, 1); + commit = lookup_commit_reference_gently(&object->oid, 1); if (commit) clear_commit_marks(commit, mark); } @@@ -599,7 -598,7 +599,7 @@@ static void record_author_date(struct a const char *ident_line; size_t ident_len; char *date_end; - unsigned long date; + timestamp_t date; ident_line = find_commit_header(buffer, "author", &ident_len); if (!ident_line) @@@ -608,7 -607,7 +608,7 @@@ !ident.date_begin || !ident.date_end) goto fail_exit; /* malformed "author" line */ - date = strtoul(ident.date_begin, &date_end, 10); + date = parse_timestamp(ident.date_begin, &date_end, 10); if (date_end != ident.date_end) goto fail_exit; /* malformed date */ *(author_date_slab_at(author_date, commit)) = date; @@@ -622,8 -621,8 +622,8 @@@ static int compare_commits_by_author_da { const struct commit *a = a_, *b = b_; struct author_date_slab *author_date = cb_data; - unsigned long a_date = *(author_date_slab_at(author_date, a)); - unsigned long b_date = *(author_date_slab_at(author_date, b)); + timestamp_t a_date = *(author_date_slab_at(author_date, a)); + timestamp_t b_date = *(author_date_slab_at(author_date, b)); /* newer commits with larger date first */ if (a_date < b_date) @@@ -1590,7 -1589,7 +1590,7 @@@ struct commit *get_merge_parent(const c struct object_id oid; if (get_sha1(name, oid.hash)) return NULL; - obj = parse_object(oid.hash); + obj = parse_object(&oid); commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); if (commit && !commit->util) set_merge_remote_desc(commit, name, obj); @@@ -1649,9 -1648,10 +1649,9 @@@ const char *find_commit_header(const ch /* * Inspect the given string and determine the true "end" of the log message, in * order to find where to put a new Signed-off-by: line. Ignored are - * trailing comment lines and blank lines, and also the traditional - * "Conflicts:" block that is not commented out, so that we can use - * "git commit -s --amend" on an existing commit that forgot to remove - * it. + * trailing comment lines and blank lines. To support "git commit -s + * --amend" on an existing commit, we also ignore "Conflicts:". To + * support "git commit -v", we truncate at cut lines. * * Returns the number of bytes from the tail to ignore, to be fed as * the second parameter to append_signoff(). @@@ -1661,9 -1661,8 +1661,9 @@@ int ignore_non_trailer(const char *buf int boc = 0; int bol = 0; int in_old_conflicts_block = 0; + size_t cutoff = wt_status_locate_end(buf, len); - while (bol < len) { + while (bol < cutoff) { const char *next_line = memchr(buf + bol, '\n', len - bol); if (!next_line) @@@ -1689,5 -1688,5 +1689,5 @@@ } bol = next_line - buf; } - return boc ? len - boc : 0; + return boc ? len - boc : len - cutoff; } diff --combined compat/mingw.c index c6134f7c81,b604a827e8..8b6fa0db44 --- a/compat/mingw.c +++ b/compat/mingw.c @@@ -423,6 -423,8 +423,8 @@@ FILE *mingw_fopen (const char *filename return NULL; } file = _wfopen(wfilename, wotype); + if (!file && GetLastError() == ERROR_INVALID_NAME) + errno = ENOENT; if (file && hide && set_hidden_flag(wfilename, 1)) warning("could not mark '%s' as hidden.", filename); return file; @@@ -940,15 -942,65 +942,15 @@@ static const char *parse_interpreter(co return p+1; } -/* - * Splits the PATH into parts. - */ -static char **get_path_split(void) -{ - char *p, **path, *envpath = mingw_getenv("PATH"); - int i, n = 0; - - if (!envpath || !*envpath) - return NULL; - - envpath = xstrdup(envpath); - p = envpath; - while (p) { - char *dir = p; - p = strchr(p, ';'); - if (p) *p++ = '\0'; - if (*dir) { /* not earlier, catches series of ; */ - ++n; - } - } - if (!n) - return NULL; - - ALLOC_ARRAY(path, n + 1); - p = envpath; - i = 0; - do { - if (*p) - path[i++] = xstrdup(p); - p = p+strlen(p)+1; - } while (i < n); - path[i] = NULL; - - free(envpath); - - return path; -} - -static void free_path_split(char **path) -{ - char **p = path; - - if (!path) - return; - - while (*p) - free(*p++); - free(path); -} - /* * exe_only means that we only want to detect .exe files, but not scripts * (which do not have an extension) */ -static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only) +static char *lookup_prog(const char *dir, int dirlen, const char *cmd, + int isexe, int exe_only) { char path[MAX_PATH]; - snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd); + snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd); if (!isexe && access(path, F_OK) == 0) return xstrdup(path); @@@ -963,29 -1015,17 +965,29 @@@ * Determines the absolute path of cmd using the split path in path. * If cmd contains a slash or backslash, no lookup is performed. */ -static char *path_lookup(const char *cmd, char **path, int exe_only) +static char *path_lookup(const char *cmd, int exe_only) { + const char *path; char *prog = NULL; int len = strlen(cmd); int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe"); if (strchr(cmd, '/') || strchr(cmd, '\\')) - prog = xstrdup(cmd); + return xstrdup(cmd); + + path = mingw_getenv("PATH"); + if (!path) + return NULL; - while (!prog && *path) - prog = lookup_prog(*path++, cmd, isexe, exe_only); + while (!prog) { + const char *sep = strchrnul(path, ';'); + int dirlen = sep - path; + if (dirlen) + prog = lookup_prog(path, dirlen, cmd, isexe, exe_only); + if (!*sep) + break; + path = sep + 1; + } return prog; } @@@ -1152,7 -1192,8 +1154,7 @@@ pid_t mingw_spawnvpe(const char *cmd, c int fhin, int fhout, int fherr) { pid_t pid; - char **path = get_path_split(); - char *prog = path_lookup(cmd, path, 0); + char *prog = path_lookup(cmd, 0); if (!prog) { errno = ENOENT; @@@ -1163,7 -1204,7 +1165,7 @@@ if (interpr) { const char *argv0 = argv[0]; - char *iprog = path_lookup(interpr, path, 1); + char *iprog = path_lookup(interpr, 1); argv[0] = prog; if (!iprog) { errno = ENOENT; @@@ -1181,18 -1222,21 +1183,18 @@@ fhin, fhout, fherr); free(prog); } - free_path_split(path); return pid; } static int try_shell_exec(const char *cmd, char *const *argv) { const char *interpr = parse_interpreter(cmd); - char **path; char *prog; int pid = 0; if (!interpr) return 0; - path = get_path_split(); - prog = path_lookup(interpr, path, 1); + prog = path_lookup(interpr, 1); if (prog) { int argc = 0; const char **argv2; @@@ -1211,6 -1255,7 +1213,6 @@@ free(prog); free(argv2); } - free_path_split(path); return pid; } @@@ -1232,7 -1277,8 +1234,7 @@@ int mingw_execv(const char *cmd, char * int mingw_execvp(const char *cmd, char *const *argv) { - char **path = get_path_split(); - char *prog = path_lookup(cmd, path, 0); + char *prog = path_lookup(cmd, 0); if (prog) { mingw_execv(prog, argv); @@@ -1240,6 -1286,7 +1242,6 @@@ } else errno = ENOENT; - free_path_split(path); return -1; } diff --combined config.c index 146cb3452a,e54d99d519..34a139c40b --- a/config.c +++ b/config.c @@@ -214,7 -214,6 +214,7 @@@ static int include_by_gitdir(const stru struct strbuf pattern = STRBUF_INIT; int ret = 0, prefix; const char *git_dir; + int already_tried_absolute = 0; if (opts->git_dir) git_dir = opts->git_dir; @@@ -227,7 -226,6 +227,7 @@@ strbuf_add(&pattern, cond, cond_len); prefix = prepare_include_condition_pattern(&pattern); +again: if (prefix < 0) goto done; @@@ -247,20 -245,6 +247,20 @@@ ret = !wildmatch(pattern.buf + prefix, text.buf + prefix, icase ? WM_CASEFOLD : 0, NULL); + if (!ret && !already_tried_absolute) { + /* + * We've tried e.g. matching gitdir:~/work, but if + * ~/work is a symlink to /mnt/storage/work + * strbuf_realpath() will expand it, so the rule won't + * match. Let's match against a + * strbuf_add_absolute_path() version of the path, + * which'll do the right thing + */ + strbuf_reset(&text); + strbuf_add_absolute_path(&text, git_dir); + already_tried_absolute = 1; + goto again; + } done: strbuf_release(&pattern); strbuf_release(&text); @@@ -1438,7 -1422,7 +1438,7 @@@ int git_config_from_file(config_fn_t fn int ret = -1; FILE *f; - f = fopen(filename, "r"); + f = fopen_or_warn(filename, "r"); if (f) { flockfile(f); ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data); @@@ -1981,7 -1965,7 +1981,7 @@@ int git_config_get_expiry(const char *k if (ret) return ret; if (strcmp(*output, "now")) { - unsigned long now = approxidate("now"); + timestamp_t now = approxidate("now"); if (approxidate(*output) >= now) git_die_config(key, _("Invalid %s: '%s'"), key, *output); } @@@ -2637,7 -2621,7 +2637,7 @@@ int git_config_rename_section_in_file(c struct lock_file *lock; int out_fd; char buf[1024]; - FILE *config_file; + FILE *config_file = NULL; struct stat st; if (new_name && !section_name_is_ok(new_name)) { @@@ -2656,6 -2640,9 +2656,9 @@@ } if (!(config_file = fopen(config_filename, "rb"))) { + ret = warn_on_fopen_errors(config_filename); + if (ret) + goto out; /* no config file means nothing to rename, no error */ goto commit_and_out; } @@@ -2719,14 -2706,11 +2722,14 @@@ } } fclose(config_file); + config_file = NULL; commit_and_out: if (commit_lock_file(lock) < 0) ret = error_errno("could not write config file %s", config_filename); out: + if (config_file) + fclose(config_file); rollback_lock_file(lock); out_no_rollback: free(filename_buf); diff --combined config.mak.uname index 192629f143,1743890164..d2cabe7fb4 --- a/config.mak.uname +++ b/config.mak.uname @@@ -36,6 -36,7 +36,7 @@@ ifeq ($(uname_S),Linux NEEDS_LIBRT = YesPlease HAVE_GETDELIM = YesPlease SANE_TEXT_GREP=-a + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease @@@ -43,6 -44,7 +44,7 @@@ HAVE_PATHS_H = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),UnixWare) CC = cc @@@ -108,6 -110,7 +110,7 @@@ ifeq ($(uname_S),Darwin BASIC_CFLAGS += -DPRECOMPOSE_UNICODE BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1 HAVE_BSD_SYSCTL = YesPlease + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@@ -201,6 -204,7 +204,7 @@@ ifeq ($(uname_S),FreeBSD GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes HAVE_BSD_SYSCTL = YesPlease PAGER_ENV = LESS=FRX LV=-c MORE=FRX + FREAD_READS_DIRECTORIES = UnfortunatelyYes endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease @@@ -237,7 -241,6 +241,7 @@@ ifeq ($(uname_S),AIX NO_MKDTEMP = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease + NO_REGEX = NeedsStartEnd FREAD_READS_DIRECTORIES = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV = YesPlease diff --combined diff.c index 5275c4b780,b6597ce568..e35cf6c704 --- a/diff.c +++ b/diff.c @@@ -27,7 -27,7 +27,7 @@@ #endif static int diff_detect_rename_default; -static int diff_indent_heuristic; /* experimental */ +static int diff_indent_heuristic = 1; static int diff_rename_limit_default = 400; static int diff_suppress_blank_empty; static int diff_use_color_default = -1; @@@ -290,6 -290,9 +290,6 @@@ int git_diff_ui_config(const char *var return 0; } - if (git_diff_heuristic_config(var, value, cb) < 0) - return -1; - if (!strcmp(var, "diff.wserrorhighlight")) { int val = parse_ws_error_highlight(value); if (val < 0) @@@ -348,9 -351,6 +348,9 @@@ int git_diff_basic_config(const char *v if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); + if (git_diff_heuristic_config(var, value, cb) < 0) + return -1; + return git_default_config(var, value, cb); } @@@ -911,7 -911,7 +911,7 @@@ static int fn_out_diff_words_write_help /* * '--color-words' algorithm can be described as: * - * 1. collect a the minus/plus lines of a diff hunk, divided into + * 1. collect the minus/plus lines of a diff hunk, divided into * minus-lines and plus-lines; * * 2. break both minus-lines and plus-lines into words and @@@ -4071,9 -4071,7 +4071,7 @@@ int diff_opt_parse(struct diff_options DIFF_OPT_CLR(options, FUNCCONTEXT); else if ((argcount = parse_long_opt("output", av, &optarg))) { char *path = prefix_filename(prefix, optarg); - options->file = fopen(path, "w"); - if (!options->file) - die_errno("Could not open '%s'", path); + options->file = xfopen(path, "w"); options->close_file = 1; if (options->use_color != GIT_COLOR_ALWAYS) options->use_color = GIT_COLOR_NEVER; @@@ -4807,9 -4805,7 +4805,7 @@@ void diff_flush(struct diff_options *op */ if (options->close_file) fclose(options->file); - options->file = fopen("/dev/null", "w"); - if (!options->file) - die_errno("Could not open /dev/null"); + options->file = xfopen("/dev/null", "w"); options->close_file = 1; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; @@@ -5244,7 -5240,7 +5240,7 @@@ size_t fill_textconv(struct userdiff_dr if (driver->textconv_cache && df->oid_valid) { *outbuf = notes_cache_get(driver->textconv_cache, - df->oid.hash, + &df->oid, &size); if (*outbuf) return size; @@@ -5256,7 -5252,7 +5252,7 @@@ if (driver->textconv_cache && df->oid_valid) { /* ignore errors, as we might be in a readonly repository */ - notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf, + notes_cache_put(driver->textconv_cache, &df->oid, *outbuf, size); /* * we could save up changes and flush them all at the end, @@@ -5270,29 -5266,6 +5266,29 @@@ return size; } +int textconv_object(const char *path, + unsigned mode, + const struct object_id *oid, + int oid_valid, + char **buf, + unsigned long *buf_size) +{ + struct diff_filespec *df; + struct userdiff_driver *textconv; + + df = alloc_filespec(path); + fill_filespec(df, oid->hash, oid_valid, mode); + textconv = get_textconv(df); + if (!textconv) { + free_filespec(df); + return 0; + } + + *buf_size = fill_textconv(textconv, df, buf); + free_filespec(df); + return 1; +} + void setup_diff_pager(struct diff_options *opt) { /* diff --combined dir.c index 70f2de38f2,be616e884e..1759063817 --- a/dir.c +++ b/dir.c @@@ -7,7 -7,6 +7,7 @@@ * Copyright (C) Linus Torvalds, 2005-2006 * Junio Hamano, 2005-2006 */ +#define NO_THE_INDEX_COMPATIBILITY_MACROS #include "cache.h" #include "dir.h" #include "attr.h" @@@ -46,11 -45,9 +46,11 @@@ struct cached_dir }; static enum path_treatment read_directory_recursive(struct dir_struct *dir, - const char *path, int len, struct untracked_cache_dir *untracked, + struct index_state *istate, const char *path, int len, + struct untracked_cache_dir *untracked, int check_only, const struct pathspec *pathspec); -static int get_dtype(struct dirent *de, const char *path, int len); +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len); int fspathcmp(const char *a, const char *b) { @@@ -177,9 -174,7 +177,9 @@@ char *common_prefix(const struct pathsp return len ? xmemdupz(pathspec->items[0].match, len) : NULL; } -int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec) +int fill_directory(struct dir_struct *dir, + struct index_state *istate, + const struct pathspec *pathspec) { const char *prefix; size_t prefix_len; @@@ -192,7 -187,7 +192,7 @@@ prefix = prefix_len ? pathspec->items[0].match : ""; /* Read the directory and prune it */ - read_directory(dir, prefix, prefix_len, pathspec); + read_directory(dir, istate, prefix, prefix_len, pathspec); return prefix_len; } @@@ -592,8 -587,7 +592,8 @@@ void add_exclude(const char *string, co x->el = el; } -static void *read_skip_worktree_file_from_index(const char *path, size_t *size, +static void *read_skip_worktree_file_from_index(const struct index_state *istate, + const char *path, size_t *size, struct sha1_stat *sha1_stat) { int pos, len; @@@ -602,12 -596,12 +602,12 @@@ void *data; len = strlen(path); - pos = cache_name_pos(path, len); + pos = index_name_pos(istate, path, len); if (pos < 0) return NULL; - if (!ce_skip_worktree(active_cache[pos])) + if (!ce_skip_worktree(istate->cache[pos])) return NULL; - data = read_sha1_file(active_cache[pos]->oid.hash, &type, &sz); + data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@@ -615,7 -609,7 +615,7 @@@ *size = xsize_t(sz); if (sha1_stat) { memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat)); - hashcpy(sha1_stat->sha1, active_cache[pos]->oid.hash); + hashcpy(sha1_stat->sha1, istate->cache[pos]->oid.hash); } return data; } @@@ -733,7 -727,7 +733,7 @@@ static void invalidate_directory(struc /* * Given a file with name "fname", read it (either from disk, or from - * the index if "check_index" is non-zero), parse it and store the + * an index if 'istate' is non-null), parse it and store the * exclude rules in "el". * * If "ss" is not NULL, compute SHA-1 of the exclude file and fill @@@ -741,8 -735,7 +741,8 @@@ * ss_valid is non-zero, "ss" must contain good value as input. */ static int add_excludes(const char *fname, const char *base, int baselen, - struct exclude_list *el, int check_index, + struct exclude_list *el, + struct index_state *istate, struct sha1_stat *sha1_stat) { struct stat st; @@@ -752,12 -745,12 +752,12 @@@ fd = open(fname, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0) { - if (errno != ENOENT) - warn_on_inaccessible(fname); - if (0 <= fd) + if (fd < 0) + warn_on_fopen_errors(fname); + else close(fd); - if (!check_index || - (buf = read_skip_worktree_file_from_index(fname, &size, sha1_stat)) == NULL) + if (!istate || + (buf = read_skip_worktree_file_from_index(istate, fname, &size, sha1_stat)) == NULL) return -1; if (size == 0) { free(buf); @@@ -789,15 -782,15 +789,15 @@@ if (sha1_stat) { int pos; if (sha1_stat->valid && - !match_stat_data_racy(&the_index, &sha1_stat->stat, &st)) + !match_stat_data_racy(istate, &sha1_stat->stat, &st)) ; /* no content change, ss->sha1 still good */ - else if (check_index && - (pos = cache_name_pos(fname, strlen(fname))) >= 0 && - !ce_stage(active_cache[pos]) && - ce_uptodate(active_cache[pos]) && + else if (istate && + (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 && + !ce_stage(istate->cache[pos]) && + ce_uptodate(istate->cache[pos]) && !would_convert_to_git(fname)) hashcpy(sha1_stat->sha1, - active_cache[pos]->oid.hash); + istate->cache[pos]->oid.hash); else hash_sha1_file(buf, size, "blob", sha1_stat->sha1); fill_stat_data(&sha1_stat->stat, &st); @@@ -828,9 -821,9 +828,9 @@@ int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen, struct exclude_list *el, - int check_index) + struct index_state *istate) { - return add_excludes(fname, base, baselen, el, check_index, NULL); + return add_excludes(fname, base, baselen, el, istate, NULL); } struct exclude_list *add_exclude_list(struct dir_struct *dir, @@@ -862,7 -855,7 +862,7 @@@ static void add_excludes_from_file_1(st if (!dir->untracked) dir->unmanaged_exclude_files++; el = add_exclude_list(dir, EXC_FILE, fname); - if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0) + if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0) die("cannot use %s as an exclude file", fname); } @@@ -965,8 -958,7 +965,8 @@@ static struct exclude *last_exclude_mat int pathlen, const char *basename, int *dtype, - struct exclude_list *el) + struct exclude_list *el, + struct index_state *istate) { struct exclude *exc = NULL; /* undecided */ int i; @@@ -981,7 -973,7 +981,7 @@@ if (x->flags & EXC_FLAG_MUSTBEDIR) { if (*dtype == DT_UNKNOWN) - *dtype = get_dtype(NULL, pathname, pathlen); + *dtype = get_dtype(NULL, istate, pathname, pathlen); if (*dtype != DT_DIR) continue; } @@@ -1014,18 -1006,16 +1014,18 @@@ */ int is_excluded_from_list(const char *pathname, int pathlen, const char *basename, int *dtype, - struct exclude_list *el) + struct exclude_list *el, struct index_state *istate) { struct exclude *exclude; - exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el); + exclude = last_exclude_matching_from_list(pathname, pathlen, basename, + dtype, el, istate); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return -1; /* undecided */ } static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir, + struct index_state *istate, const char *pathname, int pathlen, const char *basename, int *dtype_p) { @@@ -1037,7 -1027,7 +1037,7 @@@ for (j = group->nr - 1; j >= 0; j--) { exclude = last_exclude_matching_from_list( pathname, pathlen, basename, dtype_p, - &group->el[j]); + &group->el[j], istate); if (exclude) return exclude; } @@@ -1049,9 -1039,7 +1049,9 @@@ * Loads the per-directory exclude list for the substring of base * which has a char length of baselen. */ -static void prep_exclude(struct dir_struct *dir, const char *base, int baselen) +static void prep_exclude(struct dir_struct *dir, + struct index_state *istate, + const char *base, int baselen) { struct exclude_list_group *group; struct exclude_list *el; @@@ -1130,7 -1118,6 +1130,7 @@@ int dt = DT_DIR; dir->basebuf.buf[stk->baselen - 1] = 0; dir->exclude = last_exclude_matching_from_lists(dir, + istate, dir->basebuf.buf, stk->baselen - 1, dir->basebuf.buf + current, &dt); dir->basebuf.buf[stk->baselen - 1] = '/'; @@@ -1172,7 -1159,7 +1172,7 @@@ strbuf_addbuf(&sb, &dir->basebuf); strbuf_addstr(&sb, dir->exclude_per_dir); el->src = strbuf_detach(&sb, NULL); - add_excludes(el->src, el->src, stk->baselen, el, 1, + add_excludes(el->src, el->src, stk->baselen, el, istate, untracked ? &sha1_stat : NULL); } /* @@@ -1207,20 -1194,19 +1207,20 @@@ * undecided. */ struct exclude *last_exclude_matching(struct dir_struct *dir, - const char *pathname, - int *dtype_p) + struct index_state *istate, + const char *pathname, + int *dtype_p) { int pathlen = strlen(pathname); const char *basename = strrchr(pathname, '/'); basename = (basename) ? basename+1 : pathname; - prep_exclude(dir, pathname, basename-pathname); + prep_exclude(dir, istate, pathname, basename-pathname); if (dir->exclude) return dir->exclude; - return last_exclude_matching_from_lists(dir, pathname, pathlen, + return last_exclude_matching_from_lists(dir, istate, pathname, pathlen, basename, dtype_p); } @@@ -1229,11 -1215,10 +1229,11 @@@ * scans all exclude lists to determine whether pathname is excluded. * Returns 1 if true, otherwise 0. */ -int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p) +int is_excluded(struct dir_struct *dir, struct index_state *istate, + const char *pathname, int *dtype_p) { struct exclude *exclude = - last_exclude_matching(dir, pathname, dtype_p); + last_exclude_matching(dir, istate, pathname, dtype_p); if (exclude) return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1; return 0; @@@ -1248,22 -1233,18 +1248,22 @@@ static struct dir_entry *dir_entry_new( return ent; } -static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) +static struct dir_entry *dir_add_name(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int len) { - if (cache_file_exists(pathname, len, ignore_case)) + if (index_file_exists(istate, pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); return dir->entries[dir->nr++] = dir_entry_new(pathname, len); } -struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len) +struct dir_entry *dir_add_ignored(struct dir_struct *dir, + struct index_state *istate, + const char *pathname, int len) { - if (!cache_name_is_other(pathname, len)) + if (!index_name_is_other(istate, pathname, len)) return NULL; ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc); @@@ -1281,15 -1262,14 +1281,15 @@@ enum exist_status * the directory name; instead, use the case insensitive * directory hash. */ -static enum exist_status directory_exists_in_index_icase(const char *dirname, int len) +static enum exist_status directory_exists_in_index_icase(struct index_state *istate, + const char *dirname, int len) { struct cache_entry *ce; - if (cache_dir_exists(dirname, len)) + if (index_dir_exists(istate, dirname, len)) return index_directory; - ce = cache_file_exists(dirname, len, ignore_case); + ce = index_file_exists(istate, dirname, len, ignore_case); if (ce && S_ISGITLINK(ce->ce_mode)) return index_gitdir; @@@ -1303,19 -1283,18 +1303,19 @@@ * the files it contains) will sort with the '/' at the * end. */ -static enum exist_status directory_exists_in_index(const char *dirname, int len) +static enum exist_status directory_exists_in_index(struct index_state *istate, + const char *dirname, int len) { int pos; if (ignore_case) - return directory_exists_in_index_icase(dirname, len); + return directory_exists_in_index_icase(istate, dirname, len); - pos = cache_name_pos(dirname, len); + pos = index_name_pos(istate, dirname, len); if (pos < 0) pos = -pos-1; - while (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos++]; + while (pos < istate->cache_nr) { + const struct cache_entry *ce = istate->cache[pos++]; unsigned char endchar; if (strncmp(ce->name, dirname, len)) @@@ -1365,13 -1344,12 +1365,13 @@@ * (c) otherwise, we recurse into it. */ static enum path_treatment treat_directory(struct dir_struct *dir, + struct index_state *istate, struct untracked_cache_dir *untracked, const char *dirname, int len, int baselen, int exclude, const struct pathspec *pathspec) { /* The "len-1" is to strip the final '/' */ - switch (directory_exists_in_index(dirname, len-1)) { + switch (directory_exists_in_index(istate, dirname, len-1)) { case index_directory: return path_recurse; @@@ -1396,7 -1374,7 +1396,7 @@@ untracked = lookup_untracked(dir->untracked, untracked, dirname + baselen, len - baselen); - return read_directory_recursive(dir, dirname, len, + return read_directory_recursive(dir, istate, dirname, len, untracked, 1, pathspec); } @@@ -1477,13 -1455,12 +1477,13 @@@ static int exclude_matches_pathspec(con return 0; } -static int get_index_dtype(const char *path, int len) +static int get_index_dtype(struct index_state *istate, + const char *path, int len) { int pos; const struct cache_entry *ce; - ce = cache_file_exists(path, len, 0); + ce = index_file_exists(istate, path, len, 0); if (ce) { if (!ce_uptodate(ce)) return DT_UNKNOWN; @@@ -1497,12 -1474,12 +1497,12 @@@ } /* Try to look it up as a directory */ - pos = cache_name_pos(path, len); + pos = index_name_pos(istate, path, len); if (pos >= 0) return DT_UNKNOWN; pos = -pos-1; - while (pos < active_nr) { - ce = active_cache[pos++]; + while (pos < istate->cache_nr) { + ce = istate->cache[pos++]; if (strncmp(ce->name, path, len)) break; if (ce->name[len] > '/') @@@ -1516,15 -1493,14 +1516,15 @@@ return DT_UNKNOWN; } -static int get_dtype(struct dirent *de, const char *path, int len) +static int get_dtype(struct dirent *de, struct index_state *istate, + const char *path, int len) { int dtype = de ? DTYPE(de) : DT_UNKNOWN; struct stat st; if (dtype != DT_UNKNOWN) return dtype; - dtype = get_index_dtype(path, len); + dtype = get_index_dtype(istate, path, len); if (dtype != DT_UNKNOWN) return dtype; if (lstat(path, &st)) @@@ -1540,17 -1516,16 +1540,17 @@@ static enum path_treatment treat_one_path(struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec, int dtype, struct dirent *de) { int exclude; - int has_path_in_index = !!cache_file_exists(path->buf, path->len, ignore_case); + int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case); if (dtype == DT_UNKNOWN) - dtype = get_dtype(de, path->buf, path->len); + dtype = get_dtype(de, istate, path->buf, path->len); /* Always exclude indexed files */ if (dtype != DT_DIR && has_path_in_index) @@@ -1577,10 -1552,10 +1577,10 @@@ if ((dir->flags & DIR_COLLECT_KILLED_ONLY) && (dtype == DT_DIR) && !has_path_in_index && - (directory_exists_in_index(path->buf, path->len) == index_nonexistent)) + (directory_exists_in_index(istate, path->buf, path->len) == index_nonexistent)) return path_none; - exclude = is_excluded(dir, path->buf, &dtype); + exclude = is_excluded(dir, istate, path->buf, &dtype); /* * Excluded? If we don't explicitly want to show @@@ -1594,7 -1569,7 +1594,7 @@@ return path_none; case DT_DIR: strbuf_addch(path, '/'); - return treat_directory(dir, untracked, path->buf, path->len, + return treat_directory(dir, istate, untracked, path->buf, path->len, baselen, exclude, pathspec); case DT_REG: case DT_LNK: @@@ -1605,7 -1580,6 +1605,7 @@@ static enum path_treatment treat_path_fast(struct dir_struct *dir, struct untracked_cache_dir *untracked, struct cached_dir *cdir, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec) @@@ -1624,7 -1598,7 +1624,7 @@@ * to its bottom. Verify again the same set of directories * with check_only set. */ - return read_directory_recursive(dir, path->buf, path->len, + return read_directory_recursive(dir, istate, path->buf, path->len, cdir->ucd, 1, pathspec); /* * We get path_recurse in the first run when @@@ -1638,7 -1612,6 +1638,7 @@@ static enum path_treatment treat_path(struct dir_struct *dir, struct untracked_cache_dir *untracked, struct cached_dir *cdir, + struct index_state *istate, struct strbuf *path, int baselen, const struct pathspec *pathspec) @@@ -1647,7 -1620,7 +1647,7 @@@ struct dirent *de = cdir->de; if (!de) - return treat_path_fast(dir, untracked, cdir, path, + return treat_path_fast(dir, untracked, cdir, istate, path, baselen, pathspec); if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git")) return path_none; @@@ -1657,7 -1630,7 +1657,7 @@@ return path_none; dtype = DTYPE(de); - return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de); + return treat_one_path(dir, untracked, istate, path, baselen, pathspec, dtype, de); } static void add_untracked(struct untracked_cache_dir *dir, const char *name) @@@ -1671,7 -1644,6 +1671,7 @@@ static int valid_cached_dir(struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int check_only) { @@@ -1686,7 -1658,7 +1686,7 @@@ return 0; } if (!untracked->valid || - match_stat_data_racy(&the_index, &untracked->stat_data, &st)) { + match_stat_data_racy(istate, &untracked->stat_data, &st)) { if (untracked->valid) invalidate_directory(dir->untracked, untracked); fill_stat_data(&untracked->stat_data, &st); @@@ -1707,10 -1679,10 +1707,10 @@@ */ if (path->len && path->buf[path->len - 1] != '/') { strbuf_addch(path, '/'); - prep_exclude(dir, path->buf, path->len); + prep_exclude(dir, istate, path->buf, path->len); strbuf_setlen(path, path->len - 1); } else - prep_exclude(dir, path->buf, path->len); + prep_exclude(dir, istate, path->buf, path->len); /* hopefully prep_exclude() haven't invalidated this entry... */ return untracked->valid; @@@ -1719,13 -1691,12 +1719,13 @@@ static int open_cached_dir(struct cached_dir *cdir, struct dir_struct *dir, struct untracked_cache_dir *untracked, + struct index_state *istate, struct strbuf *path, int check_only) { memset(cdir, 0, sizeof(*cdir)); cdir->untracked = untracked; - if (valid_cached_dir(dir, untracked, path, check_only)) + if (valid_cached_dir(dir, untracked, istate, path, check_only)) return 0; cdir->fdir = opendir(path->len ? path->buf : "."); if (dir->untracked) @@@ -1788,9 -1759,9 +1788,9 @@@ static void close_cached_dir(struct cac * Returns the most significant path_treatment value encountered in the scan. */ static enum path_treatment read_directory_recursive(struct dir_struct *dir, - const char *base, int baselen, - struct untracked_cache_dir *untracked, int check_only, - const struct pathspec *pathspec) + struct index_state *istate, const char *base, int baselen, + struct untracked_cache_dir *untracked, int check_only, + const struct pathspec *pathspec) { struct cached_dir cdir; enum path_treatment state, subdir_state, dir_state = path_none; @@@ -1798,7 -1769,7 +1798,7 @@@ strbuf_add(&path, base, baselen); - if (open_cached_dir(&cdir, dir, untracked, &path, check_only)) + if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only)) goto out; if (untracked) @@@ -1806,23 -1777,20 +1806,23 @@@ while (!read_cached_dir(&cdir)) { /* check how the file or directory should be treated */ - state = treat_path(dir, untracked, &cdir, &path, + state = treat_path(dir, untracked, &cdir, istate, &path, baselen, pathspec); if (state > dir_state) dir_state = state; /* recurse into subdir if instructed by treat_path */ - if (state == path_recurse) { + if ((state == path_recurse) || + ((state == path_untracked) && + (dir->flags & DIR_SHOW_IGNORED_TOO) && + (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR))) { struct untracked_cache_dir *ud; ud = lookup_untracked(dir->untracked, untracked, path.buf + baselen, path.len - baselen); subdir_state = - read_directory_recursive(dir, path.buf, + read_directory_recursive(dir, istate, path.buf, path.len, ud, check_only, pathspec); if (subdir_state > dir_state) @@@ -1844,18 -1812,18 +1844,18 @@@ switch (state) { case path_excluded: if (dir->flags & DIR_SHOW_IGNORED) - dir_add_name(dir, path.buf, path.len); + dir_add_name(dir, istate, path.buf, path.len); else if ((dir->flags & DIR_SHOW_IGNORED_TOO) || ((dir->flags & DIR_COLLECT_IGNORED) && exclude_matches_pathspec(path.buf, path.len, pathspec))) - dir_add_ignored(dir, path.buf, path.len); + dir_add_ignored(dir, istate, path.buf, path.len); break; case path_untracked: if (dir->flags & DIR_SHOW_IGNORED) break; - dir_add_name(dir, path.buf, path.len); + dir_add_name(dir, istate, path.buf, path.len); if (cdir.fdir) add_untracked(untracked, path.buf + baselen); break; @@@ -1871,7 -1839,7 +1871,7 @@@ return dir_state; } -static int cmp_name(const void *p1, const void *p2) +int cmp_dir_entry(const void *p1, const void *p2) { const struct dir_entry *e1 = *(const struct dir_entry **)p1; const struct dir_entry *e2 = *(const struct dir_entry **)p2; @@@ -1879,16 -1847,7 +1879,16 @@@ return name_compare(e1->name, e1->len, e2->name, e2->len); } +/* check if *out lexically strictly contains *in */ +int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in) +{ + return (out->len < in->len) && + (out->name[out->len - 1] == '/') && + !memcmp(out->name, in->name, out->len); +} + static int treat_leading_path(struct dir_struct *dir, + struct index_state *istate, const char *path, int len, const struct pathspec *pathspec) { @@@ -1916,7 -1875,7 +1916,7 @@@ break; if (simplify_away(sb.buf, sb.len, pathspec)) break; - if (treat_one_path(dir, NULL, &sb, baselen, pathspec, + if (treat_one_path(dir, NULL, istate, &sb, baselen, pathspec, DT_DIR, NULL) == path_none) break; /* do not recurse into it */ if (len <= baselen) { @@@ -2084,8 -2043,8 +2084,8 @@@ static struct untracked_cache_dir *vali return root; } -int read_directory(struct dir_struct *dir, const char *path, - int len, const struct pathspec *pathspec) +int read_directory(struct dir_struct *dir, struct index_state *istate, + const char *path, int len, const struct pathspec *pathspec) { struct untracked_cache_dir *untracked; @@@ -2099,34 -2058,10 +2099,34 @@@ * e.g. prep_exclude() */ dir->untracked = NULL; - if (!len || treat_leading_path(dir, path, len, pathspec)) - read_directory_recursive(dir, path, len, untracked, 0, pathspec); - QSORT(dir->entries, dir->nr, cmp_name); - QSORT(dir->ignored, dir->ignored_nr, cmp_name); + if (!len || treat_leading_path(dir, istate, path, len, pathspec)) + read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec); + QSORT(dir->entries, dir->nr, cmp_dir_entry); + QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry); + + /* + * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will + * also pick up untracked contents of untracked dirs; by default + * we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not. + */ + if ((dir->flags & DIR_SHOW_IGNORED_TOO) && + !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) { + int i, j; + + /* remove from dir->entries untracked contents of untracked dirs */ + for (i = j = 0; j < dir->nr; j++) { + if (i && + check_dir_entry_contains(dir->entries[i - 1], dir->entries[j])) { + free(dir->entries[j]); + dir->entries[j] = NULL; + } else { + dir->entries[i++] = dir->entries[j]; + } + } + + dir->nr = i; + } + if (dir->untracked) { static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS); trace_printf_key(&trace_untracked_stats, @@@ -2138,12 -2073,12 +2138,12 @@@ dir->untracked->gitignore_invalidated, dir->untracked->dir_invalidated, dir->untracked->dir_opened); - if (dir->untracked == the_index.untracked && + if (dir->untracked == istate->untracked && (dir->untracked->dir_opened || dir->untracked->gitignore_invalidated || dir->untracked->dir_invalidated)) - the_index.cache_changed |= UNTRACKED_CHANGED; - if (dir->untracked != the_index.untracked) { + istate->cache_changed |= UNTRACKED_CHANGED; + if (dir->untracked != istate->untracked) { free(dir->untracked); dir->untracked = NULL; } @@@ -2337,7 -2272,7 +2337,7 @@@ int remove_path(const char *name { char *slash; - if (unlink(name) && errno != ENOENT && errno != ENOTDIR) + if (unlink(name) && !is_missing_file_error(errno)) return -1; slash = strrchr(name, '/'); diff --combined fast-import.c index e69d219682,420b3a00d3..9a22fc92c0 --- a/fast-import.c +++ b/fast-import.c @@@ -226,7 -226,7 +226,7 @@@ struct tree_entry struct atom_str *name; struct tree_entry_ms { uint16_t mode; - unsigned char sha1[20]; + struct object_id oid; } versions[2]; }; @@@ -252,19 -252,19 +252,19 @@@ struct branch unsigned active : 1; unsigned delete : 1; unsigned pack_id : PACK_ID_BITS; - unsigned char sha1[20]; + struct object_id oid; }; struct tag { struct tag *next_tag; const char *name; unsigned int pack_id; - unsigned char sha1[20]; + struct object_id oid; }; struct hash_list { struct hash_list *next; - unsigned char sha1[20]; + struct object_id oid; }; typedef enum { @@@ -386,15 -386,13 +386,15 @@@ static void write_branch_report(FILE *r fputs(" active", rpt); if (b->branch_tree.tree) fputs(" loaded", rpt); - if (is_null_sha1(b->branch_tree.versions[1].sha1)) + if (is_null_oid(&b->branch_tree.versions[1].oid)) fputs(" dirty", rpt); fputc('\n', rpt); - fprintf(rpt, " tip commit : %s\n", sha1_to_hex(b->sha1)); - fprintf(rpt, " old tree : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1)); - fprintf(rpt, " cur tree : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1)); + fprintf(rpt, " tip commit : %s\n", oid_to_hex(&b->oid)); + fprintf(rpt, " old tree : %s\n", + oid_to_hex(&b->branch_tree.versions[0].oid)); + fprintf(rpt, " cur tree : %s\n", + oid_to_hex(&b->branch_tree.versions[1].oid)); fprintf(rpt, " commit clock: %" PRIuMAX "\n", b->last_commit); fputs(" last pack : ", rpt); @@@ -472,7 -470,7 +472,7 @@@ static void write_crash_report(const ch fputs("Annotated Tags\n", rpt); fputs("--------------\n", rpt); for (tg = first_tag; tg; tg = tg->next_tag) { - fputs(sha1_to_hex(tg->sha1), rpt); + fputs(oid_to_hex(&tg->oid), rpt); fputc(' ', rpt); fputs(tg->name, rpt); fputc('\n', rpt); @@@ -557,7 -555,7 +557,7 @@@ static void alloc_objects(unsigned int alloc_count += cnt; } -static struct object_entry *new_object(unsigned char *sha1) +static struct object_entry *new_object(struct object_id *oid) { struct object_entry *e; @@@ -565,32 -563,32 +565,32 @@@ alloc_objects(object_entry_alloc); e = blocks->next_free++; - hashcpy(e->idx.sha1, sha1); + oidcpy(&e->idx.oid, oid); return e; } -static struct object_entry *find_object(unsigned char *sha1) +static struct object_entry *find_object(struct object_id *oid) { - unsigned int h = sha1[0] << 8 | sha1[1]; + unsigned int h = oid->hash[0] << 8 | oid->hash[1]; struct object_entry *e; for (e = object_table[h]; e; e = e->next) - if (!hashcmp(sha1, e->idx.sha1)) + if (!oidcmp(oid, &e->idx.oid)) return e; return NULL; } -static struct object_entry *insert_object(unsigned char *sha1) +static struct object_entry *insert_object(struct object_id *oid) { - unsigned int h = sha1[0] << 8 | sha1[1]; + unsigned int h = oid->hash[0] << 8 | oid->hash[1]; struct object_entry *e = object_table[h]; while (e) { - if (!hashcmp(sha1, e->idx.sha1)) + if (!oidcmp(oid, &e->idx.oid)) return e; e = e->next; } - e = new_object(sha1); + e = new_object(oid); e->next = object_table[h]; e->idx.offset = 0; object_table[h] = e; @@@ -878,7 -876,7 +878,7 @@@ static struct tree_content *dup_tree_co a = s->entries[i]; b = new_tree_entry(); memcpy(b, a, sizeof(*a)); - if (a->tree && is_null_sha1(b->versions[1].sha1)) + if (a->tree && is_null_oid(&b->versions[1].oid)) b->tree = dup_tree_content(a->tree); else b->tree = NULL; @@@ -1007,17 -1005,17 +1007,17 @@@ static void end_packfile(void clear_delta_base_cache(); if (object_count) { struct packed_git *new_p; - unsigned char cur_pack_sha1[20]; + struct object_id cur_pack_oid; char *idx_name; int i; struct branch *b; struct tag *t; close_pack_windows(pack_data); - sha1close(pack_file, cur_pack_sha1, 0); + sha1close(pack_file, cur_pack_oid.hash, 0); fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1, pack_data->pack_name, object_count, - cur_pack_sha1, pack_size); + cur_pack_oid.hash, pack_size); if (object_count <= unpack_limit) { if (!loosen_small_pack(pack_data)) { @@@ -1043,14 -1041,12 +1043,14 @@@ for (i = 0; i < branch_table_sz; i++) { for (b = branch_table[i]; b; b = b->table_next_branch) { if (b->pack_id == pack_id) - fprintf(pack_edges, " %s", sha1_to_hex(b->sha1)); + fprintf(pack_edges, " %s", + oid_to_hex(&b->oid)); } } for (t = first_tag; t; t = t->next_tag) { if (t->pack_id == pack_id) - fprintf(pack_edges, " %s", sha1_to_hex(t->sha1)); + fprintf(pack_edges, " %s", + oid_to_hex(&t->oid)); } fputc('\n', pack_edges); fflush(pack_edges); @@@ -1083,13 -1079,13 +1083,13 @@@ static int store_object enum object_type type, struct strbuf *dat, struct last_object *last, - unsigned char *sha1out, + struct object_id *oidout, uintmax_t mark) { void *out, *delta; struct object_entry *e; unsigned char hdr[96]; - unsigned char sha1[20]; + struct object_id oid; unsigned long hdrlen, deltalen; git_SHA_CTX c; git_zstream s; @@@ -1099,17 -1095,17 +1099,17 @@@ git_SHA1_Init(&c); git_SHA1_Update(&c, hdr, hdrlen); git_SHA1_Update(&c, dat->buf, dat->len); - git_SHA1_Final(sha1, &c); - if (sha1out) - hashcpy(sha1out, sha1); + git_SHA1_Final(oid.hash, &c); + if (oidout) + oidcpy(oidout, &oid); - e = insert_object(sha1); + e = insert_object(&oid); if (mark) insert_mark(mark, e); if (e->idx.offset) { duplicate_count_by_type[type]++; return 1; - } else if (find_sha1_pack(sha1, packed_git)) { + } else if (find_sha1_pack(oid.hash, packed_git)) { e->type = type; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@@ -1222,13 -1218,13 +1222,13 @@@ static void truncate_pack(struct sha1fi pack_size = checkpoint->offset; } -static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark) +static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) { size_t in_sz = 64 * 1024, out_sz = 64 * 1024; unsigned char *in_buf = xmalloc(in_sz); unsigned char *out_buf = xmalloc(out_sz); struct object_entry *e; - unsigned char sha1[20]; + struct object_id oid; unsigned long hdrlen; off_t offset; git_SHA_CTX c; @@@ -1291,12 -1287,12 +1291,12 @@@ } } git_deflate_end(&s); - git_SHA1_Final(sha1, &c); + git_SHA1_Final(oid.hash, &c); - if (sha1out) - hashcpy(sha1out, sha1); + if (oidout) + oidcpy(oidout, &oid); - e = insert_object(sha1); + e = insert_object(&oid); if (mark) insert_mark(mark, e); @@@ -1305,7 -1301,7 +1305,7 @@@ duplicate_count_by_type[OBJ_BLOB]++; truncate_pack(&checkpoint); - } else if (find_sha1_pack(sha1, packed_git)) { + } else if (find_sha1_pack(oid.hash, packed_git)) { e->type = OBJ_BLOB; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@@ -1389,7 -1385,7 +1389,7 @@@ static const char *get_mode(const char static void load_tree(struct tree_entry *root) { - unsigned char *sha1 = root->versions[1].sha1; + struct object_id *oid = &root->versions[1].oid; struct object_entry *myoe; struct tree_content *t; unsigned long size; @@@ -1397,22 -1393,22 +1397,22 @@@ const char *c; root->tree = t = new_tree_content(8); - if (is_null_sha1(sha1)) + if (is_null_oid(oid)) return; - myoe = find_object(sha1); + myoe = find_object(oid); if (myoe && myoe->pack_id != MAX_PACK_ID) { if (myoe->type != OBJ_TREE) - die("Not a tree: %s", sha1_to_hex(sha1)); + die("Not a tree: %s", oid_to_hex(oid)); t->delta_depth = myoe->depth; buf = gfi_unpack_entry(myoe, &size); if (!buf) - die("Can't load tree %s", sha1_to_hex(sha1)); + die("Can't load tree %s", oid_to_hex(oid)); } else { enum object_type type; - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); if (!buf || type != OBJ_TREE) - die("Can't load tree %s", sha1_to_hex(sha1)); + die("Can't load tree %s", oid_to_hex(oid)); } c = buf; @@@ -1426,13 -1422,13 +1426,13 @@@ e->tree = NULL; c = get_mode(c, &e->versions[1].mode); if (!c) - die("Corrupt mode in %s", sha1_to_hex(sha1)); + die("Corrupt mode in %s", oid_to_hex(oid)); e->versions[0].mode = e->versions[1].mode; e->name = to_atom(c, strlen(c)); c += e->name->str_len + 1; - hashcpy(e->versions[0].sha1, (unsigned char *)c); - hashcpy(e->versions[1].sha1, (unsigned char *)c); - c += 20; + hashcpy(e->versions[0].oid.hash, (unsigned char *)c); + hashcpy(e->versions[1].oid.hash, (unsigned char *)c); + c += GIT_SHA1_RAWSZ; } free(buf); } @@@ -1479,7 -1475,7 +1479,7 @@@ static void mktree(struct tree_content strbuf_addf(b, "%o %s%c", (unsigned int)(e->versions[v].mode & ~NO_DELTA), e->name->str_dat, '\0'); - strbuf_add(b, e->versions[v].sha1, 20); + strbuf_add(b, e->versions[v].oid.hash, GIT_SHA1_RAWSZ); } } @@@ -1490,7 -1486,7 +1490,7 @@@ static void store_tree(struct tree_entr struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 }; struct object_entry *le = NULL; - if (!is_null_sha1(root->versions[1].sha1)) + if (!is_null_oid(&root->versions[1].oid)) return; if (!root->tree) @@@ -1503,7 -1499,7 +1503,7 @@@ } if (!(root->versions[0].mode & NO_DELTA)) - le = find_object(root->versions[0].sha1); + le = find_object(&root->versions[0].oid); if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { mktree(t, 0, &old_tree); lo.data = old_tree; @@@ -1512,14 -1508,14 +1512,14 @@@ } mktree(t, 1, &new_tree); - store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0); + store_object(OBJ_TREE, &new_tree, &lo, &root->versions[1].oid, 0); t->delta_depth = lo.depth; for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { struct tree_entry *e = t->entries[i]; if (e->versions[1].mode) { e->versions[0].mode = e->versions[1].mode; - hashcpy(e->versions[0].sha1, e->versions[1].sha1); + oidcpy(&e->versions[0].oid, &e->versions[1].oid); t->entries[j++] = e; } else { release_tree_entry(e); @@@ -1531,14 -1527,14 +1531,14 @@@ static void tree_content_replace( struct tree_entry *root, - const unsigned char *sha1, + const struct object_id *oid, const uint16_t mode, struct tree_content *newtree) { if (!S_ISDIR(mode)) die("Root cannot be a non-directory"); - hashclr(root->versions[0].sha1); - hashcpy(root->versions[1].sha1, sha1); + oidclr(&root->versions[0].oid); + oidcpy(&root->versions[1].oid, oid); if (root->tree) release_tree_content_recursive(root->tree); root->tree = newtree; @@@ -1547,7 -1543,7 +1547,7 @@@ static int tree_content_set( struct tree_entry *root, const char *p, - const unsigned char *sha1, + const struct object_id *oid, const uint16_t mode, struct tree_content *subtree) { @@@ -1572,10 -1568,10 +1572,10 @@@ if (!*slash1) { if (!S_ISDIR(mode) && e->versions[1].mode == mode - && !hashcmp(e->versions[1].sha1, sha1)) + && !oidcmp(&e->versions[1].oid, oid)) return 0; e->versions[1].mode = mode; - hashcpy(e->versions[1].sha1, sha1); + oidcpy(&e->versions[1].oid, oid); if (e->tree) release_tree_content_recursive(e->tree); e->tree = subtree; @@@ -1596,7 -1592,7 +1596,7 @@@ if (S_ISDIR(e->versions[0].mode)) e->versions[0].mode |= NO_DELTA; - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } if (!S_ISDIR(e->versions[1].mode)) { @@@ -1605,8 -1601,8 +1605,8 @@@ } if (!e->tree) load_tree(e); - if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) { - hashclr(root->versions[1].sha1); + if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) { + oidclr(&root->versions[1].oid); return 1; } return 0; @@@ -1618,18 -1614,18 +1618,18 @@@ e = new_tree_entry(); e->name = to_atom(p, n); e->versions[0].mode = 0; - hashclr(e->versions[0].sha1); + oidclr(&e->versions[0].oid); t->entries[t->entry_count++] = e; if (*slash1) { e->tree = new_tree_content(8); e->versions[1].mode = S_IFDIR; - tree_content_set(e, slash1 + 1, sha1, mode, subtree); + tree_content_set(e, slash1 + 1, oid, mode, subtree); } else { e->tree = subtree; e->versions[1].mode = mode; - hashcpy(e->versions[1].sha1, sha1); + oidcpy(&e->versions[1].oid, oid); } - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } @@@ -1674,7 -1670,7 +1674,7 @@@ static int tree_content_remove if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { for (n = 0; n < e->tree->entry_count; n++) { if (e->tree->entries[n]->versions[1].mode) { - hashclr(root->versions[1].sha1); + oidclr(&root->versions[1].oid); return 1; } } @@@ -1693,8 -1689,8 +1693,8 @@@ del_entry release_tree_content_recursive(e->tree); e->tree = NULL; e->versions[1].mode = 0; - hashclr(e->versions[1].sha1); - hashclr(root->versions[1].sha1); + oidclr(&e->versions[1].oid); + oidclr(&root->versions[1].oid); return 1; } @@@ -1739,7 -1735,7 +1739,7 @@@ static int tree_content_get found_entry: memcpy(leaf, e, sizeof(*leaf)); - if (e->tree && is_null_sha1(e->versions[1].sha1)) + if (e->tree && is_null_oid(&e->versions[1].oid)) leaf->tree = dup_tree_content(e->tree); else leaf->tree = NULL; @@@ -1750,35 -1746,34 +1750,35 @@@ static int update_branch(struct branch { static const char *msg = "fast-import"; struct ref_transaction *transaction; - unsigned char old_sha1[20]; + struct object_id old_oid; struct strbuf err = STRBUF_INIT; - if (is_null_sha1(b->sha1)) { + if (is_null_oid(&b->oid)) { if (b->delete) delete_ref(NULL, b->name, NULL, 0); return 0; } - if (read_ref(b->name, old_sha1)) - hashclr(old_sha1); - if (!force_update && !is_null_sha1(old_sha1)) { + if (read_ref(b->name, old_oid.hash)) + oidclr(&old_oid); + if (!force_update && !is_null_oid(&old_oid)) { struct commit *old_cmit, *new_cmit; - old_cmit = lookup_commit_reference_gently(old_sha1, 0); - new_cmit = lookup_commit_reference_gently(b->sha1, 0); + old_cmit = lookup_commit_reference_gently(&old_oid, 0); + new_cmit = lookup_commit_reference_gently(&b->oid, 0); if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); if (!in_merge_bases(old_cmit, new_cmit)) { warning("Not updating %s" " (new tip %s does not contain %s)", - b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1)); + b->name, oid_to_hex(&b->oid), + oid_to_hex(&old_oid)); return -1; } } transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_update(transaction, b->name, b->sha1, old_sha1, + ref_transaction_update(transaction, b->name, b->oid.hash, old_oid.hash, 0, msg, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@@ -1820,7 -1815,7 +1820,7 @@@ static void dump_tags(void strbuf_addf(&ref_name, "refs/tags/%s", t->name); if (ref_transaction_update(transaction, ref_name.buf, - t->sha1, NULL, 0, msg, &err)) { + t->oid.hash, NULL, 0, msg, &err)) { failure |= error("%s", err.buf); goto cleanup; } @@@ -1849,7 -1844,7 +1849,7 @@@ static void dump_marks_helper(FILE *f for (k = 0; k < 1024; k++) { if (m->data.marked[k]) fprintf(f, ":%" PRIuMAX " %s\n", base + k, - sha1_to_hex(m->data.marked[k]->idx.sha1)); + oid_to_hex(&m->data.marked[k]->idx.oid)); } } } @@@ -1898,7 -1893,7 +1898,7 @@@ static void read_marks(void while (fgets(line, sizeof(line), f)) { uintmax_t mark; char *end; - unsigned char sha1[20]; + struct object_id oid; struct object_entry *e; end = strchr(line, '\n'); @@@ -1907,14 -1902,14 +1907,14 @@@ *end = 0; mark = strtoumax(line + 1, &end, 10); if (!mark || end == line + 1 - || *end != ' ' || get_sha1_hex(end + 1, sha1)) + || *end != ' ' || get_oid_hex(end + 1, &oid)) die("corrupt mark line: %s", line); - e = find_object(sha1); + e = find_object(&oid); if (!e) { - enum object_type type = sha1_object_info(sha1, NULL); + enum object_type type = sha1_object_info(oid.hash, NULL); if (type < 0) - die("object not found: %s", sha1_to_hex(sha1)); - e = insert_object(sha1); + die("object not found: %s", oid_to_hex(&oid)); + e = insert_object(&oid); e->type = type; e->pack_id = MAX_PACK_ID; e->idx.offset = 1; /* just not zero! */ @@@ -2122,21 -2117,21 +2122,21 @@@ static char *parse_ident(const char *bu static void parse_and_store_blob( struct last_object *last, - unsigned char *sha1out, + struct object_id *oidout, uintmax_t mark) { static struct strbuf buf = STRBUF_INIT; uintmax_t len; if (parse_data(&buf, big_file_threshold, &len)) - store_object(OBJ_BLOB, &buf, last, sha1out, mark); + store_object(OBJ_BLOB, &buf, last, oidout, mark); else { if (last) { strbuf_release(&last->data); last->offset = 0; last->depth = 0; } - stream_blob(len, sha1out, mark); + stream_blob(len, oidout, mark); skip_optional_lf(); } } @@@ -2212,21 -2207,21 +2212,21 @@@ static void construct_path_with_fanout( path[i++] = '/'; fanout--; } - memcpy(path + i, hex_sha1 + j, 40 - j); - path[i + 40 - j] = '\0'; + memcpy(path + i, hex_sha1 + j, GIT_SHA1_HEXSZ - j); + path[i + GIT_SHA1_HEXSZ - j] = '\0'; } static uintmax_t do_change_note_fanout( struct tree_entry *orig_root, struct tree_entry *root, - char *hex_sha1, unsigned int hex_sha1_len, + char *hex_oid, unsigned int hex_oid_len, char *fullpath, unsigned int fullpath_len, unsigned char fanout) { struct tree_content *t; struct tree_entry *e, leaf; - unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len; + unsigned int i, tmp_hex_oid_len, tmp_fullpath_len; uintmax_t num_notes = 0; - unsigned char sha1[20]; + struct object_id oid; char realpath[60]; if (!root->tree) @@@ -2235,7 -2230,7 +2235,7 @@@ for (i = 0; t && i < t->entry_count; i++) { e = t->entries[i]; - tmp_hex_sha1_len = hex_sha1_len + e->name->str_len; + tmp_hex_oid_len = hex_oid_len + e->name->str_len; tmp_fullpath_len = fullpath_len; /* @@@ -2247,12 -2242,12 +2247,12 @@@ * of 2 chars. */ if (!e->versions[1].mode || - tmp_hex_sha1_len > 40 || + tmp_hex_oid_len > GIT_SHA1_HEXSZ || e->name->str_len % 2) continue; /* This _may_ be a note entry, or a subdir containing notes */ - memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat, + memcpy(hex_oid + hex_oid_len, e->name->str_dat, e->name->str_len); if (tmp_fullpath_len) fullpath[tmp_fullpath_len++] = '/'; @@@ -2261,14 -2256,14 +2261,14 @@@ tmp_fullpath_len += e->name->str_len; fullpath[tmp_fullpath_len] = '\0'; - if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) { + if (tmp_hex_oid_len == GIT_SHA1_HEXSZ && !get_oid_hex(hex_oid, &oid)) { /* This is a note entry */ if (fanout == 0xff) { /* Counting mode, no rename */ num_notes++; continue; } - construct_path_with_fanout(hex_sha1, fanout, realpath); + construct_path_with_fanout(hex_oid, fanout, realpath); if (!strcmp(fullpath, realpath)) { /* Note entry is in correct location */ num_notes++; @@@ -2279,13 -2274,13 +2279,13 @@@ if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) die("Failed to remove path %s", fullpath); tree_content_set(orig_root, realpath, - leaf.versions[1].sha1, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); } else if (S_ISDIR(e->versions[1].mode)) { /* This is a subdir that may contain note entries */ num_notes += do_change_note_fanout(orig_root, e, - hex_sha1, tmp_hex_sha1_len, + hex_oid, tmp_hex_oid_len, fullpath, tmp_fullpath_len, fanout); } @@@ -2298,14 -2293,8 +2298,14 @@@ static uintmax_t change_note_fanout(struct tree_entry *root, unsigned char fanout) { - char hex_sha1[40], path[60]; - return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout); + /* + * The size of path is due to one slash between every two hex digits, + * plus the terminating NUL. Note that there is no slash at the end, so + * the number of slashes is one less than half the number of hex + * characters. + */ + char hex_oid[GIT_MAX_HEXSZ], path[GIT_MAX_HEXSZ + (GIT_MAX_HEXSZ / 2) - 1 + 1]; + return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout); } /* @@@ -2366,7 -2355,7 +2366,7 @@@ static void file_change_m(const char *p static struct strbuf uq = STRBUF_INIT; const char *endp; struct object_entry *oe; - unsigned char sha1[20]; + struct object_id oid; uint16_t mode, inline_data = 0; p = get_mode(p, &mode); @@@ -2389,14 -2378,15 +2389,14 @@@ if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(sha1, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - oe = find_object(sha1); - p += 40; + oe = find_object(&oid); if (*p++ != ' ') die("Missing space after SHA1: %s", command_buf.buf); } @@@ -2409,7 -2399,7 +2409,7 @@@ } /* Git does not track empty, non-toplevel directories. */ - if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) { + if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) { tree_content_remove(&b->branch_tree, p, NULL, 0); return; } @@@ -2436,12 -2426,12 +2436,12 @@@ p = uq.buf; } read_next_command(); - parse_and_store_blob(&last_blob, sha1, 0); + parse_and_store_blob(&last_blob, &oid, 0); } else { enum object_type expected = S_ISDIR(mode) ? OBJ_TREE: OBJ_BLOB; enum object_type type = oe ? oe->type : - sha1_object_info(sha1, NULL); + sha1_object_info(oid.hash, NULL); if (type < 0) die("%s not found: %s", S_ISDIR(mode) ? "Tree" : "Blob", @@@ -2453,10 -2443,10 +2453,10 @@@ } if (!*p) { - tree_content_replace(&b->branch_tree, sha1, mode, NULL); + tree_content_replace(&b->branch_tree, &oid, mode, NULL); return; } - tree_content_set(&b->branch_tree, p, sha1, mode, NULL); + tree_content_set(&b->branch_tree, p, &oid, mode, NULL); } static void file_change_d(const char *p, struct branch *b) @@@ -2514,13 -2504,13 +2514,13 @@@ static void file_change_cr(const char * die("Path %s not in branch", s); if (!*d) { /* C "path/to/subdir" "" */ tree_content_replace(&b->branch_tree, - leaf.versions[1].sha1, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); return; } tree_content_set(&b->branch_tree, d, - leaf.versions[1].sha1, + &leaf.versions[1].oid, leaf.versions[1].mode, leaf.tree); } @@@ -2530,7 -2520,7 +2530,7 @@@ static void note_change_n(const char *p static struct strbuf uq = STRBUF_INIT; struct object_entry *oe; struct branch *s; - unsigned char sha1[20], commit_sha1[20]; + struct object_id oid, commit_oid; char path[60]; uint16_t inline_data = 0; unsigned char new_fanout; @@@ -2555,14 -2545,15 +2555,14 @@@ /* or 'inline' */ if (*p == ':') { oe = find_mark(parse_mark_ref_space(&p)); - hashcpy(sha1, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else if (skip_prefix(p, "inline ", &p)) { inline_data = 1; oe = NULL; /* not used with inline_data, but makes gcc happy */ } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - oe = find_object(sha1); - p += 40; + oe = find_object(&oid); if (*p++ != ' ') die("Missing space after SHA1: %s", command_buf.buf); } @@@ -2570,19 -2561,19 +2570,19 @@@ /* */ s = lookup_branch(p); if (s) { - if (is_null_sha1(s->sha1)) + if (is_null_oid(&s->oid)) die("Can't add a note on empty branch."); - hashcpy(commit_sha1, s->sha1); + oidcpy(&commit_oid, &s->oid); } else if (*p == ':') { uintmax_t commit_mark = parse_mark_ref_eol(p); struct object_entry *commit_oe = find_mark(commit_mark); if (commit_oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", commit_mark); - hashcpy(commit_sha1, commit_oe->idx.sha1); - } else if (!get_sha1(p, commit_sha1)) { + oidcpy(&commit_oid, &commit_oe->idx.oid); + } else if (!get_oid(p, &commit_oid)) { unsigned long size; - char *buf = read_object_with_reference(commit_sha1, - commit_type, &size, commit_sha1); + char *buf = read_object_with_reference(commit_oid.hash, + commit_type, &size, commit_oid.hash); if (!buf || size < 46) die("Not a valid commit: %s", p); free(buf); @@@ -2595,13 -2586,13 +2595,13 @@@ p = uq.buf; } read_next_command(); - parse_and_store_blob(&last_blob, sha1, 0); + parse_and_store_blob(&last_blob, &oid, 0); } else if (oe) { if (oe->type != OBJ_BLOB) die("Not a blob (actually a %s): %s", typename(oe->type), command_buf.buf); - } else if (!is_null_sha1(sha1)) { - enum object_type type = sha1_object_info(sha1, NULL); + } else if (!is_null_oid(&oid)) { + enum object_type type = sha1_object_info(oid.hash, NULL); if (type < 0) die("Blob not found: %s", command_buf.buf); if (type != OBJ_BLOB) @@@ -2609,51 -2600,50 +2609,51 @@@ typename(type), command_buf.buf); } - construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path); + construct_path_with_fanout(oid_to_hex(&commit_oid), *old_fanout, path); if (tree_content_remove(&b->branch_tree, path, NULL, 0)) b->num_notes--; - if (is_null_sha1(sha1)) + if (is_null_oid(&oid)) return; /* nothing to insert */ b->num_notes++; new_fanout = convert_num_notes_to_fanout(b->num_notes); - construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path); - tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL); + construct_path_with_fanout(oid_to_hex(&commit_oid), new_fanout, path); + tree_content_set(&b->branch_tree, path, &oid, S_IFREG | 0644, NULL); } static void file_change_deleteall(struct branch *b) { release_tree_content_recursive(b->branch_tree.tree); - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); load_tree(&b->branch_tree); b->num_notes = 0; } static void parse_from_commit(struct branch *b, char *buf, unsigned long size) { - if (!buf || size < 46) - die("Not a valid commit: %s", sha1_to_hex(b->sha1)); + if (!buf || size < GIT_SHA1_HEXSZ + 6) + die("Not a valid commit: %s", oid_to_hex(&b->oid)); if (memcmp("tree ", buf, 5) - || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1)) - die("The commit %s is corrupt", sha1_to_hex(b->sha1)); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); + || get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid)) + die("The commit %s is corrupt", oid_to_hex(&b->oid)); + oidcpy(&b->branch_tree.versions[0].oid, + &b->branch_tree.versions[1].oid); } static void parse_from_existing(struct branch *b) { - if (is_null_sha1(b->sha1)) { - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + if (is_null_oid(&b->oid)) { + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); } else { unsigned long size; char *buf; - buf = read_object_with_reference(b->sha1, - commit_type, &size, b->sha1); + buf = read_object_with_reference(b->oid.hash, + commit_type, &size, + b->oid.hash); parse_from_commit(b, buf, size); free(buf); } @@@ -2663,28 -2653,28 +2663,28 @@@ static int parse_from(struct branch *b { const char *from; struct branch *s; - unsigned char sha1[20]; + struct object_id oid; if (!skip_prefix(command_buf.buf, "from ", &from)) return 0; - hashcpy(sha1, b->branch_tree.versions[1].sha1); + oidcpy(&oid, &b->branch_tree.versions[1].oid); s = lookup_branch(from); if (b == s) die("Can't create a branch from itself: %s", b->name); else if (s) { - unsigned char *t = s->branch_tree.versions[1].sha1; - hashcpy(b->sha1, s->sha1); - hashcpy(b->branch_tree.versions[0].sha1, t); - hashcpy(b->branch_tree.versions[1].sha1, t); + struct object_id *t = &s->branch_tree.versions[1].oid; + oidcpy(&b->oid, &s->oid); + oidcpy(&b->branch_tree.versions[0].oid, t); + oidcpy(&b->branch_tree.versions[1].oid, t); } else if (*from == ':') { uintmax_t idnum = parse_mark_ref_eol(from); struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - if (hashcmp(b->sha1, oe->idx.sha1)) { - hashcpy(b->sha1, oe->idx.sha1); + if (oidcmp(&b->oid, &oe->idx.oid)) { + oidcpy(&b->oid, &oe->idx.oid); if (oe->pack_id != MAX_PACK_ID) { unsigned long size; char *buf = gfi_unpack_entry(oe, &size); @@@ -2693,15 -2683,15 +2693,15 @@@ } else parse_from_existing(b); } - } else if (!get_sha1(from, b->sha1)) { + } else if (!get_oid(from, &b->oid)) { parse_from_existing(b); - if (is_null_sha1(b->sha1)) + if (is_null_oid(&b->oid)) b->delete = 1; } else die("Invalid ref name or SHA1 expression: %s", from); - if (b->branch_tree.tree && hashcmp(sha1, b->branch_tree.versions[1].sha1)) { + if (b->branch_tree.tree && oidcmp(&oid, &b->branch_tree.versions[1].oid)) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; } @@@ -2721,17 -2711,17 +2721,17 @@@ static struct hash_list *parse_merge(un n = xmalloc(sizeof(*n)); s = lookup_branch(from); if (s) - hashcpy(n->sha1, s->sha1); + oidcpy(&n->oid, &s->oid); else if (*from == ':') { uintmax_t idnum = parse_mark_ref_eol(from); struct object_entry *oe = find_mark(idnum); if (oe->type != OBJ_COMMIT) die("Mark :%" PRIuMAX " not a commit", idnum); - hashcpy(n->sha1, oe->idx.sha1); - } else if (!get_sha1(from, n->sha1)) { + oidcpy(&n->oid, &oe->idx.oid); + } else if (!get_oid(from, &n->oid)) { unsigned long size; - char *buf = read_object_with_reference(n->sha1, - commit_type, &size, n->sha1); + char *buf = read_object_with_reference(n->oid.hash, + commit_type, &size, n->oid.hash); if (!buf || size < 46) die("Not a valid commit: %s", from); free(buf); @@@ -2818,19 -2808,17 +2818,19 @@@ static void parse_new_commit(const cha /* build the tree and the commit */ store_tree(&b->branch_tree); - hashcpy(b->branch_tree.versions[0].sha1, - b->branch_tree.versions[1].sha1); + oidcpy(&b->branch_tree.versions[0].oid, + &b->branch_tree.versions[1].oid); strbuf_reset(&new_data); strbuf_addf(&new_data, "tree %s\n", - sha1_to_hex(b->branch_tree.versions[1].sha1)); - if (!is_null_sha1(b->sha1)) - strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1)); + oid_to_hex(&b->branch_tree.versions[1].oid)); + if (!is_null_oid(&b->oid)) + strbuf_addf(&new_data, "parent %s\n", + oid_to_hex(&b->oid)); while (merge_list) { struct hash_list *next = merge_list->next; - strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1)); + strbuf_addf(&new_data, "parent %s\n", + oid_to_hex(&merge_list->oid)); free(merge_list); merge_list = next; } @@@ -2843,7 -2831,7 +2843,7 @@@ free(author); free(committer); - if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark)) + if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark)) b->pack_id = pack_id; b->last_commit = object_count_by_type[OBJ_COMMIT]; } @@@ -2856,7 -2844,7 +2856,7 @@@ static void parse_new_tag(const char *a struct branch *s; struct tag *t; uintmax_t from_mark = 0; - unsigned char sha1[20]; + struct object_id oid; enum object_type type; const char *v; @@@ -2875,20 -2863,20 +2875,20 @@@ die("Expected from command, got %s", command_buf.buf); s = lookup_branch(from); if (s) { - if (is_null_sha1(s->sha1)) + if (is_null_oid(&s->oid)) die("Can't tag an empty branch."); - hashcpy(sha1, s->sha1); + oidcpy(&oid, &s->oid); type = OBJ_COMMIT; } else if (*from == ':') { struct object_entry *oe; from_mark = parse_mark_ref_eol(from); oe = find_mark(from_mark); type = oe->type; - hashcpy(sha1, oe->idx.sha1); - } else if (!get_sha1(from, sha1)) { - struct object_entry *oe = find_object(sha1); + oidcpy(&oid, &oe->idx.oid); + } else if (!get_oid(from, &oid)) { + struct object_entry *oe = find_object(&oid); if (!oe) { - type = sha1_object_info(sha1, NULL); + type = sha1_object_info(oid.hash, NULL); if (type < 0) die("Not a valid object: %s", from); } else @@@ -2914,7 -2902,7 +2914,7 @@@ "object %s\n" "type %s\n" "tag %s\n", - sha1_to_hex(sha1), typename(type), t->name); + oid_to_hex(&oid), typename(type), t->name); if (tagger) strbuf_addf(&new_data, "tagger %s\n", tagger); @@@ -2922,7 -2910,7 +2922,7 @@@ strbuf_addbuf(&new_data, &msg); free(tagger); - if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0)) + if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0)) t->pack_id = MAX_PACK_ID; else t->pack_id = pack_id; @@@ -2934,9 -2922,9 +2934,9 @@@ static void parse_reset_branch(const ch b = lookup_branch(arg); if (b) { - hashclr(b->sha1); - hashclr(b->branch_tree.versions[0].sha1); - hashclr(b->branch_tree.versions[1].sha1); + oidclr(&b->oid); + oidclr(&b->branch_tree.versions[0].oid); + oidclr(&b->branch_tree.versions[1].oid); if (b->branch_tree.tree) { release_tree_content_recursive(b->branch_tree.tree); b->branch_tree.tree = NULL; @@@ -2956,7 -2944,7 +2956,7 @@@ static void cat_blob_write(const char * die_errno("Write to frontend failed"); } -static void cat_blob(struct object_entry *oe, unsigned char sha1[20]) +static void cat_blob(struct object_entry *oe, struct object_id *oid) { struct strbuf line = STRBUF_INIT; unsigned long size; @@@ -2964,7 -2952,7 +2964,7 @@@ char *buf; if (!oe || oe->pack_id == MAX_PACK_ID) { - buf = read_sha1_file(sha1, &type, &size); + buf = read_sha1_file(oid->hash, &type, &size); } else { type = oe->type; buf = gfi_unpack_entry(oe, &size); @@@ -2975,19 -2963,19 +2975,19 @@@ */ if (type <= 0) { strbuf_reset(&line); - strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1)); + strbuf_addf(&line, "%s missing\n", oid_to_hex(oid)); cat_blob_write(line.buf, line.len); strbuf_release(&line); free(buf); return; } if (!buf) - die("Can't read object %s", sha1_to_hex(sha1)); + die("Can't read object %s", oid_to_hex(oid)); if (type != OBJ_BLOB) die("Object %s is a %s but a blob was expected.", - sha1_to_hex(sha1), typename(type)); + oid_to_hex(oid), typename(type)); strbuf_reset(&line); - strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1), + strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid), typename(type), size); cat_blob_write(line.buf, line.len); strbuf_release(&line); @@@ -3004,7 -2992,7 +3004,7 @@@ static void parse_get_mark(const char *p) { struct object_entry *oe = oe; - char output[42]; + char output[GIT_MAX_HEXSZ + 2]; /* get-mark SP LF */ if (*p != ':') @@@ -3014,43 -3002,43 +3014,43 @@@ if (!oe) die("Unknown mark: %s", command_buf.buf); - xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1)); - cat_blob_write(output, 41); + xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid)); + cat_blob_write(output, GIT_SHA1_HEXSZ + 1); } static void parse_cat_blob(const char *p) { struct object_entry *oe = oe; - unsigned char sha1[20]; + struct object_id oid; /* cat-blob SP LF */ if (*p == ':') { oe = find_mark(parse_mark_ref_eol(p)); if (!oe) die("Unknown mark: %s", command_buf.buf); - hashcpy(sha1, oe->idx.sha1); + oidcpy(&oid, &oe->idx.oid); } else { - if (get_sha1_hex(p, sha1)) + if (parse_oid_hex(p, &oid, &p)) die("Invalid dataref: %s", command_buf.buf); - if (p[40]) + if (*p) die("Garbage after SHA1: %s", command_buf.buf); - oe = find_object(sha1); + oe = find_object(&oid); } - cat_blob(oe, sha1); + cat_blob(oe, &oid); } static struct object_entry *dereference(struct object_entry *oe, - unsigned char sha1[20]) + struct object_id *oid) { unsigned long size; char *buf = NULL; if (!oe) { - enum object_type type = sha1_object_info(sha1, NULL); + enum object_type type = sha1_object_info(oid->hash, NULL); if (type < 0) - die("object not found: %s", sha1_to_hex(sha1)); + die("object not found: %s", oid_to_hex(oid)); /* cache it! */ - oe = insert_object(sha1); + oe = insert_object(oid); oe->type = type; oe->pack_id = MAX_PACK_ID; oe->idx.offset = 1; @@@ -3069,48 -3057,49 +3069,48 @@@ buf = gfi_unpack_entry(oe, &size); } else { enum object_type unused; - buf = read_sha1_file(sha1, &unused, &size); + buf = read_sha1_file(oid->hash, &unused, &size); } if (!buf) - die("Can't load object %s", sha1_to_hex(sha1)); + die("Can't load object %s", oid_to_hex(oid)); /* Peel one layer. */ switch (oe->type) { case OBJ_TAG: - if (size < 40 + strlen("object ") || - get_sha1_hex(buf + strlen("object "), sha1)) + if (size < GIT_SHA1_HEXSZ + strlen("object ") || + get_oid_hex(buf + strlen("object "), oid)) die("Invalid SHA1 in tag: %s", command_buf.buf); break; case OBJ_COMMIT: - if (size < 40 + strlen("tree ") || - get_sha1_hex(buf + strlen("tree "), sha1)) + if (size < GIT_SHA1_HEXSZ + strlen("tree ") || + get_oid_hex(buf + strlen("tree "), oid)) die("Invalid SHA1 in commit: %s", command_buf.buf); } free(buf); - return find_object(sha1); + return find_object(oid); } static struct object_entry *parse_treeish_dataref(const char **p) { - unsigned char sha1[20]; + struct object_id oid; struct object_entry *e; if (**p == ':') { /* */ e = find_mark(parse_mark_ref_space(p)); if (!e) die("Unknown mark: %s", command_buf.buf); - hashcpy(sha1, e->idx.sha1); + oidcpy(&oid, &e->idx.oid); } else { /* */ - if (get_sha1_hex(*p, sha1)) + if (parse_oid_hex(*p, &oid, p)) die("Invalid dataref: %s", command_buf.buf); - e = find_object(sha1); - *p += 40; + e = find_object(&oid); if (*(*p)++ != ' ') die("Missing space after tree-ish: %s", command_buf.buf); } while (!e || e->type != OBJ_TREE) - e = dereference(e, sha1); + e = dereference(e, &oid); return e; } @@@ -3154,8 -3143,8 +3154,8 @@@ static void parse_ls(const char *p, str } else { struct object_entry *e = parse_treeish_dataref(&p); root = new_tree_entry(); - hashcpy(root->versions[1].sha1, e->idx.sha1); - if (!is_null_sha1(root->versions[1].sha1)) + oidcpy(&root->versions[1].oid, &e->idx.oid); + if (!is_null_oid(&root->versions[1].oid)) root->versions[1].mode = S_IFDIR; load_tree(root); } @@@ -3177,7 -3166,7 +3177,7 @@@ if (S_ISDIR(leaf.versions[1].mode)) store_tree(&leaf); - print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p); + print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p); if (leaf.tree) release_tree_content_recursive(leaf.tree); if (!b || root != &b->branch_tree) @@@ -3285,9 -3274,7 +3285,7 @@@ static void option_export_pack_edges(co { if (pack_edges) fclose(pack_edges); - pack_edges = fopen(edges, "a"); - if (!pack_edges) - die_errno("Cannot open '%s'", edges); + pack_edges = xfopen(edges, "a"); } static int parse_one_option(const char *option) diff --combined git-compat-util.h index e83fd2eb07,87f47828a5..51ba4e6b3b --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -319,11 -319,6 +319,11 @@@ extern char *gitdirname(char *) #define PRIo32 "o" #endif +typedef uintmax_t timestamp_t; +#define PRItime PRIuMAX +#define parse_timestamp strtoumax +#define TIME_MAX UINTMAX_MAX + #ifndef PATH_SEP #define PATH_SEP ':' #endif @@@ -450,6 -445,7 +450,6 @@@ extern void (*get_error_routine(void))( extern void set_warn_routine(void (*routine)(const char *warn, va_list params)); extern void (*get_warn_routine(void))(const char *warn, va_list params); extern void set_die_is_recursing_routine(int (*routine)(void)); -extern void set_error_handle(FILE *); extern int starts_with(const char *str, const char *prefix); @@@ -620,7 -616,7 +620,7 @@@ extern int git_lstat(const char *, stru #endif #define DEFAULT_PACKED_GIT_LIMIT \ - ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256)) + ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256)) #ifdef NO_PREAD #define pread git_pread @@@ -693,10 -689,12 +693,12 @@@ char *gitstrdup(const char *s) #endif #ifdef FREAD_READS_DIRECTORIES - #ifdef fopen - #undef fopen - #endif - #define fopen(a,b) git_fopen(a,b) + # if !defined(SUPPRESS_FOPEN_REDEFINITION) + # ifdef fopen + # undef fopen + # endif + # define fopen(a,b) git_fopen(a,b) + # endif extern FILE *git_fopen(const char*, const char*); #endif @@@ -804,6 -802,7 +806,7 @@@ extern int xmkstemp(char *template) extern int xmkstemp_mode(char *template, int mode); extern char *xgetcwd(void); extern FILE *fopen_for_writing(const char *path); + extern FILE *fopen_or_warn(const char *path, const char *mode); #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc))) #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc))) @@@ -1068,15 -1067,6 +1071,15 @@@ static inline int regexec_buf(const reg #define HAVE_VARIADIC_MACROS 1 #endif +#ifdef HAVE_VARIADIC_MACROS +__attribute__((format (printf, 3, 4))) NORETURN +void BUG_fl(const char *file, int line, const char *fmt, ...); +#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__) +#else +__attribute__((format (printf, 1, 2))) NORETURN +void BUG(const char *fmt, ...); +#endif + /* * Preserves errno, prints a message, but gives no warning for ENOENT. * Returns 0 on success, which includes trying to unlink an object that does @@@ -1110,8 -1100,8 +1113,8 @@@ int remove_or_warn(unsigned int mode, c int access_or_warn(const char *path, int mode, unsigned flag); int access_or_die(const char *path, int mode, unsigned flag); - /* Warn on an inaccessible file that ought to be accessible */ - void warn_on_inaccessible(const char *path); + /* Warn on an inaccessible file if errno indicates this is an error */ + int warn_on_fopen_errors(const char *path); #ifdef GMTIME_UNRELIABLE_ERRORS struct tm *git_gmtime(const time_t *); @@@ -1134,21 -1124,6 +1137,21 @@@ struct tm *git_gmtime_r(const time_t * #define getc_unlocked(fh) getc(fh) #endif +/* + * Our code often opens a path to an optional file, to work on its + * contents when we can successfully open it. We can ignore a failure + * to open if such an optional file does not exist, but we do want to + * report a failure in opening for other reasons (e.g. we got an I/O + * error, or the file is there, but we lack the permission to open). + * + * Call this function after seeing an error from open() or fopen() to + * see if the errno indicates a missing file that we can safely ignore. + */ +static inline int is_missing_file_error(int errno_) +{ + return (errno_ == ENOENT || errno_ == ENOTDIR); +} + extern int cmd_main(int, const char **); #endif diff --combined remote.c index 3649d60cdc,1f2453d0f6..f998f989e9 --- a/remote.c +++ b/remote.c @@@ -251,7 -251,7 +251,7 @@@ static const char *skip_spaces(const ch static void read_remotes_file(struct remote *remote) { struct strbuf buf = STRBUF_INIT; - FILE *f = fopen(git_path("remotes/%s", remote->name), "r"); + FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r"); if (!f) return; @@@ -277,7 -277,7 +277,7 @@@ static void read_branches_file(struct r { char *frag; struct strbuf buf = STRBUF_INIT; - FILE *f = fopen(git_path("branches/%s", remote->name), "r"); + FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r"); if (!f) return; @@@ -477,6 -477,26 +477,6 @@@ static void read_config(void alias_all_urls(); } -/* - * This function frees a refspec array. - * Warning: code paths should be checked to ensure that the src - * and dst pointers are always freeable pointers as well - * as the refspec pointer itself. - */ -static void free_refspecs(struct refspec *refspec, int nr_refspec) -{ - int i; - - if (!refspec) - return; - - for (i = 0; i < nr_refspec; i++) { - free(refspec[i].src); - free(refspec[i].dst); - } - free(refspec); -} - static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) { int i; @@@ -590,7 -610,7 +590,7 @@@ * since it is only possible to reach this point from within * the for loop above. */ - free_refspecs(rs, i+1); + free_refspec(i+1, rs); return NULL; } die("Invalid refspec '%s'", refspec[i]); @@@ -601,7 -621,7 +601,7 @@@ int valid_fetch_refspec(const char *fet struct refspec *refspec; refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1); - free_refspecs(refspec, 1); + free_refspec(1, refspec); return !!refspec; } @@@ -618,10 -638,6 +618,10 @@@ struct refspec *parse_push_refspec(int void free_refspec(int nr_refspec, struct refspec *refspec) { int i; + + if (!refspec) + return; + for (i = 0; i < nr_refspec; i++) { free(refspec[i].src); free(refspec[i].dst); @@@ -633,12 -649,7 +633,12 @@@ static int valid_remote_nick(const cha { if (!name[0] || is_dot_or_dotdot(name)) return 0; - return !strchr(name, '/'); /* no slash */ + + /* remote nicknames cannot contain slashes */ + while (*name) + if (is_dir_sep(*name++)) + return 0; + return 1; } const char *remote_for_branch(struct branch *branch, int *explicit) @@@ -1180,10 -1191,9 +1180,10 @@@ static int match_explicit(struct ref *s else if (is_null_oid(&matched_src->new_oid)) error("unable to delete '%s': remote ref does not exist", dst_value); - else if ((dst_guess = guess_ref(dst_value, matched_src))) + else if ((dst_guess = guess_ref(dst_value, matched_src))) { matched_dst = make_linked_ref(dst_guess, dst_tail); - else + free(dst_guess); + } else error("unable to push to unqualified destination: %s\n" "The destination refspec neither matches an " "existing ref on the remote nor\n" @@@ -1286,7 -1296,7 +1286,7 @@@ static void add_to_tips(struct tips *ti if (is_null_oid(oid)) return; - commit = lookup_commit_reference_gently(oid->hash, 1); + commit = lookup_commit_reference_gently(oid, 1); if (!commit || (commit->object.flags & TMP_MARK)) return; commit->object.flags |= TMP_MARK; @@@ -1348,8 -1358,7 +1348,8 @@@ static void add_missing_tags(struct re if (is_null_oid(&ref->new_oid)) continue; - commit = lookup_commit_reference_gently(ref->new_oid.hash, 1); + commit = lookup_commit_reference_gently(&ref->new_oid, + 1); if (!commit) /* not pushing a commit, which is not an error */ continue; @@@ -1576,8 -1585,8 +1576,8 @@@ void set_ref_status_for_push(struct re reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; else if (!has_object_file(&ref->old_oid)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; - else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) || - !lookup_commit_reference_gently(ref->new_oid.hash, 1)) + else if (!lookup_commit_reference_gently(&ref->old_oid, 1) || + !lookup_commit_reference_gently(&ref->new_oid, 1)) reject_reason = REF_STATUS_REJECT_NEEDS_FORCE; else if (!ref_newer(&ref->new_oid, &ref->old_oid)) reject_reason = REF_STATUS_REJECT_NONFASTFORWARD; @@@ -1944,12 -1953,12 +1944,12 @@@ int ref_newer(const struct object_id *n * Both new and old must be commit-ish and new is descendant of * old. Otherwise we require --force. */ - o = deref_tag(parse_object(old_oid->hash), NULL, 0); + o = deref_tag(parse_object(old_oid), NULL, 0); if (!o || o->type != OBJ_COMMIT) return 0; old = (struct commit *) o; - o = deref_tag(parse_object(new_oid->hash), NULL, 0); + o = deref_tag(parse_object(new_oid), NULL, 0); if (!o || o->type != OBJ_COMMIT) return 0; new = (struct commit *) o; @@@ -2000,13 -2009,13 +2000,13 @@@ int stat_tracking_info(struct branch *b /* Cannot stat if what we used to build on no longer exists */ if (read_ref(base, oid.hash)) return -1; - theirs = lookup_commit_reference(oid.hash); + theirs = lookup_commit_reference(&oid); if (!theirs) return -1; if (read_ref(branch->refname, oid.hash)) return -1; - ours = lookup_commit_reference(oid.hash); + ours = lookup_commit_reference(&oid); if (!ours) return -1; diff --combined sequencer.c index 924fb1d0c3,11b5c7c114..5282fb849c --- a/sequencer.c +++ b/sequencer.c @@@ -344,7 -344,7 +344,7 @@@ static int read_oneliner(struct strbuf static struct tree *empty_tree(void) { - return lookup_tree(EMPTY_TREE_SHA1_BIN); + return lookup_tree(&empty_tree_oid); } static int error_dirty_index(struct replay_opts *opts) @@@ -374,7 -374,7 +374,7 @@@ static void update_abort_safety_file(vo write_file(git_path_abort_safety_file(), "%s", ""); } -static int fast_forward_to(const unsigned char *to, const unsigned char *from, +static int fast_forward_to(const struct object_id *to, const struct object_id *from, int unborn, struct replay_opts *opts) { struct ref_transaction *transaction; @@@ -390,7 -390,7 +390,7 @@@ transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, "HEAD", - to, unborn ? null_sha1 : from, + to->hash, unborn ? null_sha1 : from->hash, 0, sb.buf, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); @@@ -426,7 -426,7 +426,7 @@@ void append_conflicts_hint(struct strbu static int do_recursive_merge(struct commit *base, struct commit *next, const char *base_label, const char *next_label, - unsigned char *head, struct strbuf *msgbuf, + struct object_id *head, struct strbuf *msgbuf, struct replay_opts *opts) { struct merge_options o; @@@ -464,8 -464,7 +464,8 @@@ if (active_cache_changed && write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) - /* TRANSLATORS: %s will be "revert", "cherry-pick" or + /* + * TRANSLATORS: %s will be "revert", "cherry-pick" or * "rebase -i". */ return error(_("%s: Unable to write new index file"), @@@ -483,13 -482,13 +483,13 @@@ static int is_index_unchanged(void) { - unsigned char head_sha1[20]; + struct object_id head_oid; struct commit *head_commit; - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL)) + if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) return error(_("could not resolve HEAD commit\n")); - head_commit = lookup_commit(head_sha1); + head_commit = lookup_commit(&head_oid); /* * If head_commit is NULL, check_commit, called from @@@ -509,8 -508,7 +509,8 @@@ if (cache_tree_update(&the_index, 0)) return error(_("unable to update cache tree\n")); - return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash); + return !oidcmp(&active_cache_tree->oid, + &head_commit->tree->object.oid); } static int write_author_script(const char *message) @@@ -836,13 -834,13 +836,13 @@@ static int update_squash_messages(enum strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len); strbuf_release(&header); } else { - unsigned char head[20]; + struct object_id head; struct commit *head_commit; const char *head_message, *body; - if (get_sha1("HEAD", head)) + if (get_oid("HEAD", &head)) return error(_("need a HEAD to fixup")); - if (!(head_commit = lookup_commit_reference(head))) + if (!(head_commit = lookup_commit_reference(&head))) return error(_("could not read HEAD")); if (!(head_message = get_commit_buffer(head_commit, NULL))) return error(_("could not read HEAD's commit message")); @@@ -899,8 -897,8 +899,8 @@@ static void flush_rewritten_pending(voi FILE *out; if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 && - !get_sha1("HEAD", newsha1) && - (out = fopen(rebase_path_rewritten_list(), "a"))) { + !get_sha1("HEAD", newsha1) && + (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) { char *bol = buf.buf, *eol; while (*bol) { @@@ -919,7 -917,7 +919,7 @@@ static void record_in_rewritten(struct object_id *oid, enum todo_command next_command) { - FILE *out = fopen(rebase_path_rewritten_pending(), "a"); + FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a"); if (!out) return; @@@ -936,7 -934,7 +936,7 @@@ static int do_pick_commit(enum todo_com { unsigned int flags = opts->edit ? EDIT_MSG : 0; const char *msg_file = opts->edit ? NULL : git_path_merge_msg(); - unsigned char head[20]; + struct object_id head; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL }; @@@ -950,12 -948,12 +950,12 @@@ * that represents the "current" state for merge-recursive * to work on. */ - if (write_cache_as_tree(head, 0, NULL)) + if (write_cache_as_tree(head.hash, 0, NULL)) return error(_("your index file is unmerged.")); } else { - unborn = get_sha1("HEAD", head); + unborn = get_oid("HEAD", &head); if (unborn) - hashcpy(head, EMPTY_TREE_SHA1_BIN); + oidcpy(&head, &empty_tree_oid); if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0)) return error_dirty_index(opts); } @@@ -991,11 -989,11 +991,11 @@@ oid_to_hex(&commit->object.oid)); if (opts->allow_ff && !is_fixup(command) && - ((parent && !hashcmp(parent->object.oid.hash, head)) || + ((parent && !oidcmp(&parent->object.oid, &head)) || (!parent && unborn))) { if (is_rebase_i(opts)) write_author_script(msg.message); - res = fast_forward_to(commit->object.oid.hash, head, unborn, + res = fast_forward_to(&commit->object.oid, &head, unborn, opts); if (res || command != TODO_REWORD) goto leave; @@@ -1047,7 -1045,6 +1047,7 @@@ strbuf_addstr(&msgbuf, p); if (opts->record_origin) { + strbuf_complete_line(&msgbuf); if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); @@@ -1083,7 -1080,7 +1083,7 @@@ res = -1; else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, - head, &msgbuf, opts); + &head, &msgbuf, opts); if (res < 0) return res; res |= write_message(msgbuf.buf, msgbuf.len, @@@ -1099,7 -1096,7 +1099,7 @@@ commit_list_insert(next, &remotes); res |= try_merge_command(opts->strategy, opts->xopts_nr, (const char **)opts->xopts, - common, sha1_to_hex(head), remotes); + common, oid_to_hex(&head), remotes); free_commit_list(common); free_commit_list(remotes); } @@@ -1224,7 -1221,7 +1224,7 @@@ static struct todo_item *append_new_tod static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) { - unsigned char commit_sha1[20]; + struct object_id commit_oid; char *end_of_object_name; int i, saved, status, padding; @@@ -1273,7 -1270,7 +1273,7 @@@ end_of_object_name = (char *) bol + strcspn(bol, " \t\n"); saved = *end_of_object_name; *end_of_object_name = '\0'; - status = get_sha1(bol, commit_sha1); + status = get_oid(bol, &commit_oid); *end_of_object_name = saved; item->arg = end_of_object_name + strspn(end_of_object_name, " \t"); @@@ -1282,7 -1279,7 +1282,7 @@@ if (status < 0) return -1; - item->commit = lookup_commit_reference(commit_sha1); + item->commit = lookup_commit_reference(&commit_oid); return !item->commit; } @@@ -1381,7 -1378,7 +1381,7 @@@ static int read_populate_todo(struct to if (is_rebase_i(opts)) { struct todo_list done = TODO_LIST_INIT; - FILE *f = fopen(rebase_path_msgtotal(), "w"); + FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w"); if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 && !parse_insn_buffer(done.buf.buf, &done)) @@@ -1916,13 -1913,11 +1916,13 @@@ static int apply_autostash(struct repla strbuf_trim(&stash_sha1); child.git_cmd = 1; + child.no_stdout = 1; + child.no_stderr = 1; argv_array_push(&child.args, "stash"); argv_array_push(&child.args, "apply"); argv_array_push(&child.args, stash_sha1.buf); if (!run_command(&child)) - printf(_("Applied autostash.")); + printf(_("Applied autostash.\n")); else { struct child_process store = CHILD_PROCESS_INIT; @@@ -2093,7 -2088,6 +2093,7 @@@ cleanup_head_ref res = error(_("could not read orig-head")); goto cleanup_head_ref; } + strbuf_reset(&buf); if (!read_oneliner(&buf, rebase_path_onto(), 0)) { res = error(_("could not read 'onto'")); goto cleanup_head_ref; @@@ -2286,7 -2280,7 +2286,7 @@@ static int single_pick(struct commit *c int sequencer_pick_revisions(struct replay_opts *opts) { struct todo_list todo_list = TODO_LIST_INIT; - unsigned char sha1[20]; + struct object_id oid; int i, res; assert(opts->revs); @@@ -2294,16 -2288,16 +2294,16 @@@ return -1; for (i = 0; i < opts->revs->pending.nr; i++) { - unsigned char sha1[20]; + struct object_id oid; const char *name = opts->revs->pending.objects[i].name; /* This happens when using --stdin. */ if (!strlen(name)) continue; - if (!get_sha1(name, sha1)) { - if (!lookup_commit_reference_gently(sha1, 1)) { - enum object_type type = sha1_object_info(sha1, NULL); + if (!get_oid(name, &oid)) { + if (!lookup_commit_reference_gently(&oid, 1)) { + enum object_type type = sha1_object_info(oid.hash, NULL); return error(_("%s: can't cherry-pick a %s"), name, typename(type)); } @@@ -2340,9 -2334,9 +2340,9 @@@ if (walk_revs_populate_todo(&todo_list, opts) || create_seq_dir() < 0) return -1; - if (get_sha1("HEAD", sha1) && (opts->action == REPLAY_REVERT)) + if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT)) return error(_("can't revert as initial commit")); - if (save_head(sha1_to_hex(sha1))) + if (save_head(oid_to_hex(&oid))) return -1; if (save_opts(opts)) return -1; @@@ -2363,9 -2357,6 +2363,9 @@@ void append_signoff(struct strbuf *msgb getenv("GIT_COMMITTER_EMAIL"))); strbuf_addch(&sob, '\n'); + if (!ignore_footer) + strbuf_complete_line(msgbuf); + /* * If the whole message buffer is equal to the sob, pretend that we * found a conforming footer with a matching sob @@@ -2386,6 -2377,13 +2386,6 @@@ * the title and body to be filled in by the user. */ append_newlines = "\n\n"; - } else if (msgbuf->buf[len - 1] != '\n') { - /* - * Incomplete line. Complete the line and add a - * blank one so that there is an empty line between - * the message body and the sob. - */ - append_newlines = "\n\n"; } else if (len == 1) { /* * Buffer contains a single newline. Add another diff --combined server-info.c index 6f865b73a3,e01ac154a8..5ec5b1d827 --- a/server-info.c +++ b/server-info.c @@@ -53,7 -53,7 +53,7 @@@ static int add_info_ref(const char *pat int flag, void *cb_data) { FILE *fp = cb_data; - struct object *o = parse_object(oid->hash); + struct object *o = parse_object(oid); if (!o) return -1; @@@ -133,7 -133,7 +133,7 @@@ static int read_pack_info_file(const ch char line[1000]; int old_cnt = 0; - fp = fopen(infofile, "r"); + fp = fopen_or_warn(infofile, "r"); if (!fp) return 1; /* nonexistent is not an error. */ diff --combined wrapper.c index 708e98a965,b117eb14a4..4632c7d4c0 --- a/wrapper.c +++ b/wrapper.c @@@ -418,6 -418,32 +418,32 @@@ FILE *fopen_for_writing(const char *pat return ret; } + static void warn_on_inaccessible(const char *path) + { + warning_errno(_("unable to access '%s'"), path); + } + + int warn_on_fopen_errors(const char *path) + { + if (errno != ENOENT && errno != ENOTDIR) { + warn_on_inaccessible(path); + return -1; + } + + return 0; + } + + FILE *fopen_or_warn(const char *path, const char *mode) + { + FILE *fp = fopen(path, mode); + + if (fp) + return fp; + + warn_on_fopen_errors(path); + return NULL; + } + int xmkstemp(char *template) { int fd; @@@ -576,15 -602,10 +602,10 @@@ int remove_or_warn(unsigned int mode, c return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); } - void warn_on_inaccessible(const char *path) - { - warning_errno(_("unable to access '%s'"), path); - } - static int access_error_is_ok(int err, unsigned flag) { - return err == ENOENT || err == ENOTDIR || - ((flag & ACCESS_EACCES_OK) && err == EACCES); + return (is_missing_file_error(err) || + ((flag & ACCESS_EACCES_OK) && err == EACCES)); } int access_or_warn(const char *path, int mode, unsigned flag) diff --combined wt-status.c index 25aafc35c8,cdf9f5eed2..bf651f16fa --- a/wt-status.c +++ b/wt-status.c @@@ -665,7 -665,7 +665,7 @@@ static void wt_status_collect_untracked dir.untracked = the_index.untracked; setup_standard_excludes(&dir); - fill_directory(&dir, &s->pathspec); + fill_directory(&dir, &the_index, &s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; @@@ -896,18 -896,17 +896,18 @@@ conclude status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); } -void wt_status_truncate_message_at_cut_line(struct strbuf *buf) +size_t wt_status_locate_end(const char *s, size_t len) { const char *p; struct strbuf pattern = STRBUF_INIT; strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line); - if (starts_with(buf->buf, pattern.buf + 1)) - strbuf_setlen(buf, 0); - else if ((p = strstr(buf->buf, pattern.buf))) - strbuf_setlen(buf, p - buf->buf + 1); + if (starts_with(s, pattern.buf + 1)) + len = 0; + else if ((p = strstr(s, pattern.buf))) + len = p - s + 1; strbuf_release(&pattern); + return len; } void wt_status_add_cut_line(FILE *fp) @@@ -1003,7 -1002,7 +1003,7 @@@ static void wt_longstatus_print_trackin color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", comment_line_char); else - fputs("", s->fp); + fputs("\n", s->fp); } static int has_unmerged(struct wt_status *s) @@@ -1066,7 -1065,8 +1066,8 @@@ static void show_am_in_progress(struct static char *read_line_from_git_path(const char *filename) { struct strbuf buf = STRBUF_INIT; - FILE *fp = fopen(git_path("%s", filename), "r"); + FILE *fp = fopen_or_warn(git_path("%s", filename), "r"); + if (!fp) { strbuf_release(&buf); return NULL; @@@ -1083,29 -1083,29 +1084,29 @@@ static int split_commit_in_progress(struct wt_status *s) { int split_in_progress = 0; - char *head = read_line_from_git_path("HEAD"); - char *orig_head = read_line_from_git_path("ORIG_HEAD"); - char *rebase_amend = read_line_from_git_path("rebase-merge/amend"); - char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head"); + char *head, *orig_head, *rebase_amend, *rebase_orig_head; - if (!head || !orig_head || !rebase_amend || !rebase_orig_head || + if ((!s->amend && !s->nowarn && !s->workdir_dirty) || !s->branch || strcmp(s->branch, "HEAD")) - return split_in_progress; + return 0; - if (!strcmp(rebase_amend, rebase_orig_head)) { - if (strcmp(head, rebase_amend)) - split_in_progress = 1; - } else if (strcmp(orig_head, rebase_orig_head)) { - split_in_progress = 1; - } + head = read_line_from_git_path("HEAD"); + orig_head = read_line_from_git_path("ORIG_HEAD"); + rebase_amend = read_line_from_git_path("rebase-merge/amend"); + rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head"); - if (!s->amend && !s->nowarn && !s->workdir_dirty) - split_in_progress = 0; + if (!head || !orig_head || !rebase_amend || !rebase_orig_head) + ; /* fall through, no split in progress */ + else if (!strcmp(rebase_amend, rebase_orig_head)) + split_in_progress = !!strcmp(head, rebase_amend); + else if (strcmp(orig_head, rebase_orig_head)) + split_in_progress = 1; free(head); free(orig_head); free(rebase_amend); free(rebase_orig_head); + return split_in_progress; } @@@ -1169,7 -1169,6 +1170,7 @@@ static int read_rebase_todolist(const c abbrev_sha1_in_line(&line); string_list_append(lines, line.buf); } + fclose(f); return 0; } @@@ -1389,7 -1388,7 +1390,7 @@@ struct grab_1st_switch_cbdata }; static int grab_1st_switch(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct grab_1st_switch_cbdata *cb = cb_data; @@@ -1430,7 -1429,7 +1431,7 @@@ static void wt_status_get_detached_from /* sha1 is a commit? match without further lookup */ (!oidcmp(&cb.noid, &oid) || /* perhaps sha1 is a tag, try to dereference to a commit */ - ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL && + ((commit = lookup_commit_reference_gently(&oid, 1)) != NULL && !oidcmp(&cb.noid, &commit->object.oid)))) { const char *from = ref; if (!skip_prefix(from, "refs/tags/", &from))