From: Junio C Hamano Date: Tue, 26 Mar 2013 20:15:56 +0000 (-0700) Subject: Merge branch 'jc/reflog-reverse-walk' X-Git-Tag: v1.8.3-rc0~167 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/6beb484f25c9b11baf0edef4bc9cbfa7e3261df6?ds=inline;hp=-c Merge branch 'jc/reflog-reverse-walk' An internal function used to implement "git checkout @{-1}" was hard to use correctly. * jc/reflog-reverse-walk: refs.c: fix fread error handling reflog: add for_each_reflog_ent_reverse() API for_each_recent_reflog_ent(): simplify opening of a reflog file for_each_reflog_ent(): extract a helper to process a single entry --- 6beb484f25c9b11baf0edef4bc9cbfa7e3261df6 diff --combined refs.c index e2b760d0ba,aa79c93045..de2d8eb866 --- a/refs.c +++ b/refs.c @@@ -3,7 -3,6 +3,7 @@@ #include "object.h" #include "tag.h" #include "dir.h" +#include "string-list.h" /* * Make sure "ref" is something reasonable to have under ".git/refs/"; @@@ -334,12 -333,14 +334,12 @@@ struct string_slice static int ref_entry_cmp_sslice(const void *key_, const void *ent_) { - struct string_slice *key = (struct string_slice *)key_; - struct ref_entry *ent = *(struct ref_entry **)ent_; - int entlen = strlen(ent->name); - int cmplen = key->len < entlen ? key->len : entlen; - int cmp = memcmp(key->str, ent->name, cmplen); + const struct string_slice *key = key_; + const struct ref_entry *ent = *(const struct ref_entry * const *)ent_; + int cmp = strncmp(key->str, ent->name, key->len); if (cmp) return cmp; - return key->len - entlen; + return '\0' - (unsigned char)ent->name[key->len]; } /* @@@ -803,38 -804,11 +803,38 @@@ static const char *parse_ref_line(char return line; } +/* + * Read f, which is a packed-refs file, into dir. + * + * A comment line of the form "# pack-refs with: " may contain zero or + * more traits. We interpret the traits as follows: + * + * No traits: + * + * Probably no references are peeled. But if the file contains a + * peeled value for a reference, we will use it. + * + * peeled: + * + * References under "refs/tags/", if they *can* be peeled, *are* + * peeled in this file. References outside of "refs/tags/" are + * probably not peeled even if they could have been, but if we find + * a peeled value for such a reference we will use it. + * + * fully-peeled: + * + * All references in the file that can be peeled are peeled. + * Inversely (and this is more important), any references in the + * file for which no peeled value is recorded is not peelable. This + * trait should typically be written alongside "peeled" for + * compatibility with older clients, but we do not require it + * (i.e., "peeled" is a no-op if "fully-peeled" is set). + */ static void read_packed_refs(FILE *f, struct ref_dir *dir) { struct ref_entry *last = NULL; char refline[PATH_MAX]; - int flag = REF_ISPACKED; + enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; while (fgets(refline, sizeof(refline), f)) { unsigned char sha1[20]; @@@ -843,20 -817,15 +843,20 @@@ if (!strncmp(refline, header, sizeof(header)-1)) { const char *traits = refline + sizeof(header) - 1; - if (strstr(traits, " peeled ")) - flag |= REF_KNOWS_PEELED; + if (strstr(traits, " fully-peeled ")) + peeled = PEELED_FULLY; + else if (strstr(traits, " peeled ")) + peeled = PEELED_TAGS; /* perhaps other traits later as well */ continue; } refname = parse_ref_line(refline, sha1); if (refname) { - last = create_ref_entry(refname, sha1, flag, 1); + last = create_ref_entry(refname, sha1, REF_ISPACKED, 1); + if (peeled == PEELED_FULLY || + (peeled == PEELED_TAGS && !prefixcmp(refname, "refs/tags/"))) + last->flag |= REF_KNOWS_PEELED; add_ref(dir, last); continue; } @@@ -864,15 -833,8 +864,15 @@@ refline[0] == '^' && strlen(refline) == 42 && refline[41] == '\n' && - !get_sha1_hex(refline + 1, sha1)) + !get_sha1_hex(refline + 1, sha1)) { hashcpy(last->u.value.peeled, sha1); + /* + * Regardless of what the file header said, + * we definitely know the value of *this* + * reference: + */ + last->flag |= REF_KNOWS_PEELED; + } } } @@@ -1240,8 -1202,6 +1240,8 @@@ int peel_ref(const char *refname, unsig if (current_ref && (current_ref->name == refname || !strcmp(current_ref->name, refname))) { if (current_ref->flag & REF_KNOWS_PEELED) { + if (is_null_sha1(current_ref->u.value.peeled)) + return -1; hashcpy(sha1, current_ref->u.value.peeled); return 0; } @@@ -1263,16 -1223,9 +1263,16 @@@ } fallback: - o = parse_object(base); - if (o && o->type == OBJ_TAG) { - o = deref_tag(o, refname, 0); + o = lookup_unknown_object(base); + if (o->type == OBJ_NONE) { + int type = sha1_object_info(base, NULL); + if (type < 0) + return -1; + o->type = type; + } + + if (o->type == OBJ_TAG) { + o = deref_tag_noverify(o); if (o) { hashcpy(sha1, o->sha1); return 0; @@@ -1782,8 -1735,7 +1782,8 @@@ static struct lock_file packlock static int repack_without_ref(const char *refname) { struct repack_without_ref_sb data; - struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL)); + struct ref_cache *refs = get_ref_cache(NULL); + struct ref_dir *packed = get_packed_refs(refs); if (find_ref(packed, refname) == NULL) return 0; data.refname = refname; @@@ -1792,8 -1744,6 +1792,8 @@@ unable_to_lock_error(git_path("packed-refs"), errno); return error("cannot delete '%s' from packed refs", refname); } + clear_packed_ref_cache(refs); + packed = get_packed_refs(refs); do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data); return commit_lock_file(&packlock); } @@@ -1803,24 -1753,32 +1803,24 @@@ int delete_ref(const char *refname, con struct ref_lock *lock; int err, i = 0, ret = 0, flag = 0; - lock = lock_ref_sha1_basic(refname, sha1, 0, &flag); + lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag); if (!lock) return 1; if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) { /* loose */ - const char *path; - - if (!(delopt & REF_NODEREF)) { - i = strlen(lock->lk->filename) - 5; /* .lock */ - lock->lk->filename[i] = 0; - path = lock->lk->filename; - } else { - path = git_path("%s", refname); - } - err = unlink_or_warn(path); + i = strlen(lock->lk->filename) - 5; /* .lock */ + lock->lk->filename[i] = 0; + err = unlink_or_warn(lock->lk->filename); if (err && errno != ENOENT) ret = 1; - if (!(delopt & REF_NODEREF)) - lock->lk->filename[i] = '.'; + lock->lk->filename[i] = '.'; } /* removing the loose one could have resurrected an earlier * packed one. Also, if it was not loose we need to repack * without it. */ - ret |= repack_without_ref(refname); + ret |= repack_without_ref(lock->ref_name); unlink_or_warn(git_path("logs/%s", lock->ref_name)); invalidate_ref_cache(NULL); @@@ -2332,59 -2290,117 +2332,117 @@@ int read_ref_at(const char *refname, un return 1; } - int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long ofs, void *cb_data) + static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) + { + unsigned char osha1[20], nsha1[20]; + char *email_end, *message; + unsigned long timestamp; + int tz; + + /* old SP new SP name SP time TAB msg LF */ + if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' || + get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' || + get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' || + !(email_end = strchr(sb->buf + 82, '>')) || + email_end[1] != ' ' || + !(timestamp = strtoul(email_end + 2, &message, 10)) || + !message || message[0] != ' ' || + (message[1] != '+' && message[1] != '-') || + !isdigit(message[2]) || !isdigit(message[3]) || + !isdigit(message[4]) || !isdigit(message[5])) + return 0; /* corrupt? */ + email_end[1] = '\0'; + tz = strtol(message + 1, NULL, 10); + if (message[6] != '\t') + message += 6; + else + message += 7; + return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data); + } + + static char *find_beginning_of_line(char *bob, char *scan) + { + while (bob < scan && *(--scan) != '\n') + ; /* keep scanning backwards */ + /* + * Return either beginning of the buffer, or LF at the end of + * the previous line. + */ + return scan; + } + + int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data) { - const char *logfile; - FILE *logfp; struct strbuf sb = STRBUF_INIT; - int ret = 0; + FILE *logfp; + long pos; + int ret = 0, at_tail = 1; - logfile = git_path("logs/%s", refname); - logfp = fopen(logfile, "r"); + logfp = fopen(git_path("logs/%s", refname), "r"); if (!logfp) return -1; - if (ofs) { - struct stat statbuf; - if (fstat(fileno(logfp), &statbuf) || - statbuf.st_size < ofs || - fseek(logfp, -ofs, SEEK_END) || - strbuf_getwholeline(&sb, logfp, '\n')) { - fclose(logfp); - strbuf_release(&sb); - return -1; + /* Jump to the end */ + if (fseek(logfp, 0, SEEK_END) < 0) + return error("cannot seek back reflog for %s: %s", + refname, strerror(errno)); + pos = ftell(logfp); + while (!ret && 0 < pos) { + int cnt; + size_t nread; + char buf[BUFSIZ]; + char *endp, *scanp; + + /* Fill next block from the end */ + cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos; + if (fseek(logfp, pos - cnt, SEEK_SET)) + return error("cannot seek back reflog for %s: %s", + refname, strerror(errno)); + nread = fread(buf, cnt, 1, logfp); + if (nread != 1) + return error("cannot read %d bytes from reflog for %s: %s", + cnt, refname, strerror(errno)); + pos -= cnt; + + scanp = endp = buf + cnt; + if (at_tail && scanp[-1] == '\n') + /* Looking at the final LF at the end of the file */ + scanp--; + at_tail = 0; + + while (buf < scanp) { + /* + * terminating LF of the previous line, or the beginning + * of the buffer. + */ + char *bp; + + bp = find_beginning_of_line(buf, scanp); + + if (*bp != '\n') { + strbuf_splice(&sb, 0, 0, buf, endp - buf); + if (pos) + break; /* need to fill another block */ + scanp = buf - 1; /* leave loop */ + } else { + /* + * (bp + 1) thru endp is the beginning of the + * current line we have in sb + */ + strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1)); + scanp = bp; + endp = bp + 1; + } + ret = show_one_reflog_ent(&sb, fn, cb_data); + strbuf_reset(&sb); + if (ret) + break; } - } - while (!strbuf_getwholeline(&sb, logfp, '\n')) { - unsigned char osha1[20], nsha1[20]; - char *email_end, *message; - unsigned long timestamp; - int tz; - - /* old SP new SP name SP time TAB msg LF */ - if (sb.len < 83 || sb.buf[sb.len - 1] != '\n' || - get_sha1_hex(sb.buf, osha1) || sb.buf[40] != ' ' || - get_sha1_hex(sb.buf + 41, nsha1) || sb.buf[81] != ' ' || - !(email_end = strchr(sb.buf + 82, '>')) || - email_end[1] != ' ' || - !(timestamp = strtoul(email_end + 2, &message, 10)) || - !message || message[0] != ' ' || - (message[1] != '+' && message[1] != '-') || - !isdigit(message[2]) || !isdigit(message[3]) || - !isdigit(message[4]) || !isdigit(message[5])) - continue; /* corrupt? */ - email_end[1] = '\0'; - tz = strtol(message + 1, NULL, 10); - if (message[6] != '\t') - message += 6; - else - message += 7; - ret = fn(osha1, nsha1, sb.buf + 82, timestamp, tz, message, - cb_data); - if (ret) - break; } + if (!ret && sb.len) + ret = show_one_reflog_ent(&sb, fn, cb_data); + fclose(logfp); strbuf_release(&sb); return ret; @@@ -2392,9 -2408,20 +2450,20 @@@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data) { - return for_each_recent_reflog_ent(refname, fn, 0, cb_data); - } + FILE *logfp; + struct strbuf sb = STRBUF_INIT; + int ret = 0; + + logfp = fopen(git_path("logs/%s", refname), "r"); + if (!logfp) + return -1; + while (!ret && !strbuf_getwholeline(&sb, logfp, '\n')) + ret = show_one_reflog_ent(&sb, fn, cb_data); + fclose(logfp); + strbuf_release(&sb); + return ret; + } /* * Call fn for each reflog in the namespace indicated by name. name * must be empty or end with '/'. Name will be used as a scratch @@@ -2594,46 -2621,3 +2663,46 @@@ char *shorten_unambiguous_ref(const cha free(short_name); return xstrdup(refname); } + +static struct string_list *hide_refs; + +int parse_hide_refs_config(const char *var, const char *value, const char *section) +{ + if (!strcmp("transfer.hiderefs", var) || + /* NEEDSWORK: use parse_config_key() once both are merged */ + (!prefixcmp(var, section) && var[strlen(section)] == '.' && + !strcmp(var + strlen(section), ".hiderefs"))) { + char *ref; + int len; + + if (!value) + return config_error_nonbool(var); + ref = xstrdup(value); + len = strlen(ref); + while (len && ref[len - 1] == '/') + ref[--len] = '\0'; + if (!hide_refs) { + hide_refs = xcalloc(1, sizeof(*hide_refs)); + hide_refs->strdup_strings = 1; + } + string_list_append(hide_refs, ref); + } + return 0; +} + +int ref_is_hidden(const char *refname) +{ + struct string_list_item *item; + + if (!hide_refs) + return 0; + for_each_string_list_item(item, hide_refs) { + int len; + if (prefixcmp(refname, item->string)) + continue; + len = strlen(item->string); + if (!refname[len] || refname[len] == '/') + return 1; + } + return 0; +} diff --combined refs.h index 1b2e2d3a98,a62b9dbd59..a35eafc4ee --- a/refs.h +++ b/refs.h @@@ -103,7 -103,7 +103,7 @@@ extern int read_ref_at(const char *refn /* iterate over reflog entries */ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data); - int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long, void *cb_data); + int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); /* * Calls the specified function for each reflog file until it returns nonzero, @@@ -147,7 -147,4 +147,7 @@@ int update_ref(const char *action, cons const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr); +extern int parse_hide_refs_config(const char *var, const char *value, const char *); +extern int ref_is_hidden(const char *); + #endif /* REFS_H */ diff --combined sha1_name.c index c50630a3ea,635cd131a6..2fbda48e02 --- a/sha1_name.c +++ b/sha1_name.c @@@ -856,8 -856,8 +856,8 @@@ static int get_sha1_oneline(const char } struct grab_nth_branch_switch_cbdata { - long cnt, alloc; - struct strbuf *buf; + int remaining; + struct strbuf buf; }; static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1, @@@ -867,7 -867,6 +867,6 @@@ struct grab_nth_branch_switch_cbdata *cb = cb_data; const char *match = NULL, *target = NULL; size_t len; - int nth; if (!prefixcmp(message, "checkout: moving from ")) { match = message + strlen("checkout: moving from "); @@@ -876,11 -875,12 +875,12 @@@ if (!match || !target) return 0; - - len = target - match; - nth = cb->cnt++ % cb->alloc; - strbuf_reset(&cb->buf[nth]); - strbuf_add(&cb->buf[nth], match, len); + if (--(cb->remaining) == 0) { + len = target - match; + strbuf_reset(&cb->buf); + strbuf_add(&cb->buf, match, len); + return 1; /* we are done */ + } return 0; } @@@ -891,7 -891,7 +891,7 @@@ static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf) { long nth; - int i, retval; + int retval; struct grab_nth_branch_switch_cbdata cb; const char *brace; char *num_end; @@@ -901,34 -901,22 +901,22 @@@ brace = strchr(name, '}'); if (!brace) return -1; - nth = strtol(name+3, &num_end, 10); + nth = strtol(name + 3, &num_end, 10); if (num_end != brace) return -1; if (nth <= 0) return -1; - cb.alloc = nth; - cb.buf = xmalloc(nth * sizeof(struct strbuf)); - for (i = 0; i < nth; i++) - strbuf_init(&cb.buf[i], 20); - cb.cnt = 0; + cb.remaining = nth; + strbuf_init(&cb.buf, 20); + retval = 0; - for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb); - if (cb.cnt < nth) { - cb.cnt = 0; - for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb); - } - if (cb.cnt < nth) - goto release_return; - i = cb.cnt % nth; - strbuf_reset(buf); - strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len); - retval = brace-name+1; - - release_return: - for (i = 0; i < nth; i++) - strbuf_release(&cb.buf[i]); - free(cb.buf); + if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) { + strbuf_reset(buf); + strbuf_add(buf, cb.buf.buf, cb.buf.len); + retval = brace - name + 1; + } + strbuf_release(&cb.buf); return retval; } @@@ -1137,8 -1125,7 +1125,8 @@@ int get_sha1_blob(const char *name, uns static void diagnose_invalid_sha1_path(const char *prefix, const char *filename, const unsigned char *tree_sha1, - const char *object_name) + const char *object_name, + int object_name_len) { struct stat st; unsigned char sha1[20]; @@@ -1148,8 -1135,8 +1136,8 @@@ prefix = ""; if (!lstat(filename, &st)) - die("Path '%s' exists on disk, but not in '%s'.", - filename, object_name); + die("Path '%s' exists on disk, but not in '%.*s'.", + filename, object_name_len, object_name); if (errno == ENOENT || errno == ENOTDIR) { char *fullname = xmalloc(strlen(filename) + strlen(prefix) + 1); @@@ -1159,16 -1146,16 +1147,16 @@@ if (!get_tree_entry(tree_sha1, fullname, sha1, &mode)) { die("Path '%s' exists, but not '%s'.\n" - "Did you mean '%s:%s' aka '%s:./%s'?", + "Did you mean '%.*s:%s' aka '%.*s:./%s'?", fullname, filename, - object_name, + object_name_len, object_name, fullname, - object_name, + object_name_len, object_name, filename); } - die("Path '%s' does not exist in '%s'", - filename, object_name); + die("Path '%s' does not exist in '%.*s'", + filename, object_name_len, object_name); } } @@@ -1333,8 -1320,13 +1321,8 @@@ static int get_sha1_with_context_1(cons } if (*cp == ':') { unsigned char tree_sha1[20]; - char *object_name = NULL; - if (only_to_die) { - object_name = xmalloc(cp-name+1); - strncpy(object_name, name, cp-name); - object_name[cp-name] = '\0'; - } - if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) { + int len = cp - name; + if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) { const char *filename = cp+1; char *new_filename = NULL; @@@ -1344,8 -1336,8 +1332,8 @@@ ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode); if (ret && only_to_die) { diagnose_invalid_sha1_path(prefix, filename, - tree_sha1, object_name); - free(object_name); + tree_sha1, + name, len); } hashcpy(oc->tree, tree_sha1); strncpy(oc->path, filename, @@@ -1356,7 -1348,7 +1344,7 @@@ return ret; } else { if (only_to_die) - die("Invalid object name '%s'.", object_name); + die("Invalid object name '%.*s'.", len, name); } } return ret;