From: Junio C Hamano Date: Mon, 25 Jul 2016 21:13:33 +0000 (-0700) Subject: Merge branch 'mh/ref-iterators' X-Git-Tag: v2.10.0-rc0~96 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/87492cb24d9d8be8e18217b89ae5f090089ff31d?hp=-c Merge branch 'mh/ref-iterators' The API to iterate over all the refs (i.e. for_each_ref(), etc.) has been revamped. * mh/ref-iterators: for_each_reflog(): reimplement using iterators dir_iterator: new API for iterating over a directory tree for_each_reflog(): don't abort for bad references do_for_each_ref(): reimplement using reference iteration refs: introduce an iterator interface ref_resolves_to_object(): new function entry_resolves_to_object(): rename function from ref_resolves_to_object() get_ref_cache(): only create an instance if there is a submodule remote rm: handle symbolic refs correctly delete_refs(): add a flags argument refs: use name "prefix" consistently do_for_each_ref(): move docstring to the header file refs: remove unnecessary "extern" keywords --- 87492cb24d9d8be8e18217b89ae5f090089ff31d diff --combined Makefile index c1e88dfcdd,b4ffc11b86..bfe85595cd --- a/Makefile +++ b/Makefile @@@ -375,7 -375,13 +375,7 @@@ GIT-VERSION-FILE: FORC # CFLAGS and LDFLAGS are for the users to override from the command line. CFLAGS = -g -O2 -Wall -LDFLAGS = -ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS) -ALL_LDFLAGS = $(LDFLAGS) -STRIP ?= strip - -ifdef DEVELOPER -CFLAGS += -Werror \ +DEVELOPER_CFLAGS = -Werror \ -Wdeclaration-after-statement \ -Wno-format-zero-length \ -Wold-style-definition \ @@@ -384,10 -390,7 +384,10 @@@ -Wstrict-prototypes \ -Wunused \ -Wvla -endif +LDFLAGS = +ALL_CFLAGS = $(CPPFLAGS) $(CFLAGS) +ALL_LDFLAGS = $(LDFLAGS) +STRIP ?= strip # Create as necessary, replace existing, make ranlib unneeded. ARFLAGS = rcs @@@ -437,6 -440,7 +437,6 @@@ DIFF = dif TAR = tar FIND = find INSTALL = install -RPMBUILD = rpmbuild TCL_PATH = tclsh TCLTK_PATH = wish XGETTEXT = xgettext @@@ -617,7 -621,7 +617,7 @@@ TEST_PROGRAMS_NEED_X += test-svn-f TEST_PROGRAMS_NEED_X += test-urlmatch-normalization TEST_PROGRAMS_NEED_X += test-wildmatch -TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X)) +TEST_PROGRAMS = $(patsubst %,t/helper/%$X,$(TEST_PROGRAMS_NEED_X)) # List built-in command $C whose implementation cmd_$C() is not in # builtin/$C.o but is linked in as part of some other command. @@@ -718,6 -722,7 +718,7 @@@ LIB_OBJS += diff-lib. LIB_OBJS += diff-no-index.o LIB_OBJS += diff.o LIB_OBJS += dir.o + LIB_OBJS += dir-iterator.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o @@@ -782,6 -787,7 +783,7 @@@ LIB_OBJS += read-cache. LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o + LIB_OBJS += refs/iterator.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace_object.o @@@ -939,7 -945,7 +941,7 @@@ BUILTIN_OBJS += builtin/verify-tag. BUILTIN_OBJS += builtin/worktree.o BUILTIN_OBJS += builtin/write-tree.o -GITLIBS = $(LIB_FILE) $(XDIFF_LIB) +GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) EXTLIBS = GIT_USER_AGENT = git/$(GIT_VERSION) @@@ -948,10 -954,6 +950,10 @@@ include config.mak.unam -include config.mak.autogen -include config.mak +ifdef DEVELOPER +CFLAGS += $(DEVELOPER_CFLAGS) +endif + ifndef sysconfdir ifeq ($(prefix),/usr) sysconfdir = /etc @@@ -1572,15 -1574,7 +1574,15 @@@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_ DIFF_SQ = $(subst ','\'',$(DIFF)) PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA)) -LIBS = $(GITLIBS) $(EXTLIBS) +# We must filter out any object files from $(GITLIBS), +# as it is typically used like: +# +# foo: foo.o $(GITLIBS) +# $(CC) $(filter %.o,$^) $(LIBS) +# +# where we use it as a dependency. Since we also pull object files +# from the dependency list, that would make each entry appear twice. +LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS) BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ $(COMPAT_CFLAGS) @@@ -1716,8 -1710,8 +1718,8 @@@ git.sp git.s git.o: EXTRA_CPPFLAGS = '-DGIT_INFO_PATH="$(infodir_relative_SQ)"' git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) - $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) git.o \ - $(BUILTIN_OBJS) $(LIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ + $(filter %.o,$^) $(LIBS) help.sp help.s help.o: common-cmds.h @@@ -1906,11 -1900,10 +1908,11 @@@ VCSSVN_OBJS += vcs-svn/fast_export. VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o -TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) +TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ + common-main.o \ git.o ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o @@@ -2073,9 -2066,6 +2075,9 @@@ XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) - XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H) LOCALIZED_SH = $(SCRIPT_SH) +LOCALIZED_SH += git-parse-remote.sh +LOCALIZED_SH += git-rebase--interactive.sh +LOCALIZED_SH += git-sh-setup.sh LOCALIZED_PERL = $(SCRIPT_PERL) ifdef XGETTEXT_INCLUDE_TESTS @@@ -2217,7 -2207,7 +2219,7 @@@ bin-wrappers/%: wrap-for-bin.s @mkdir -p bin-wrappers $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@@BUILD_DIR@@|$(shell pwd)|' \ - -e 's|@@PROG@@|$(@F)|' < $< > $@ && \ + -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \ chmod +x $@ # GNU make supports exporting all variables by "export" without parameters. @@@ -2237,25 -2227,25 +2239,25 @@@ perf: al .PHONY: test perf -test-ctype$X: ctype.o +t/helper/test-ctype$X: ctype.o -test-date$X: date.o ctype.o +t/helper/test-date$X: date.o ctype.o -test-delta$X: diff-delta.o patch-delta.o +t/helper/test-delta$X: diff-delta.o patch-delta.o -test-line-buffer$X: vcs-svn/lib.a +t/helper/test-line-buffer$X: vcs-svn/lib.a -test-parse-options$X: parse-options.o parse-options-cb.o +t/helper/test-parse-options$X: parse-options.o parse-options-cb.o -test-svn-fe$X: vcs-svn/lib.a +t/helper/test-svn-fe$X: vcs-svn/lib.a .PRECIOUS: $(TEST_OBJS) -test-%$X: test-%.o GIT-LDFLAGS $(GITLIBS) +t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS) -check-sha1:: test-sha1$X - ./test-sha1.sh +check-sha1:: t/helper/test-sha1$X + t/helper/test-sha1.sh SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ)) @@@ -2402,25 -2392,31 +2404,25 @@@ quick-install-html ### Maintainer's dist rules -git.spec: git.spec.in GIT-VERSION-FILE - sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+ - mv $@+ $@ - GIT_TARNAME = git-$(GIT_VERSION) -dist: git.spec git-archive$(X) configure +dist: git-archive$(X) configure ./git-archive --format=tar \ --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar @mkdir -p $(GIT_TARNAME) - @cp git.spec configure $(GIT_TARNAME) + @cp configure $(GIT_TARNAME) @echo $(GIT_VERSION) > $(GIT_TARNAME)/version @$(MAKE) -C git-gui TARDIR=../$(GIT_TARNAME)/git-gui dist-version $(TAR) rf $(GIT_TARNAME).tar \ - $(GIT_TARNAME)/git.spec \ $(GIT_TARNAME)/configure \ $(GIT_TARNAME)/version \ $(GIT_TARNAME)/git-gui/version @$(RM) -r $(GIT_TARNAME) gzip -f -9 $(GIT_TARNAME).tar -rpm: dist - $(RPMBUILD) \ - --define "_source_filedigest_algorithm md5" \ - --define "_binary_filedigest_algorithm md5" \ - -ta $(GIT_TARNAME).tar.gz +rpm:: + @echo >&2 "Use distro packaged sources to run rpmbuild" + @false +.PHONY: rpm htmldocs = git-htmldocs-$(GIT_VERSION) manpages = git-manpages-$(GIT_VERSION) @@@ -2456,8 -2452,8 +2458,8 @@@ profile-clean $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs))) clean: profile-clean coverage-clean - $(RM) *.o *.res refs/*.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o - $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o + $(RM) *.res + $(RM) $(OBJECTS) $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(NO_INSTALL) @@@ -2496,7 -2492,6 +2498,7 @@@ ALL_COMMANDS += git-gui git-citoo .PHONY: check-docs check-docs:: + $(MAKE) -C Documentation lint-docs @(for v in $(ALL_COMMANDS); \ do \ case "$$v" in \ diff --combined builtin/fetch.c index d9ea6f392a,b55c83c468..acd0cf1755 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@@ -15,7 -15,6 +15,7 @@@ #include "submodule.h" #include "connected.h" #include "argv-array.h" +#include "utf8.h" static const char * const builtin_fetch_usage[] = { N_("git fetch [] [ [...]]"), @@@ -450,132 -449,7 +450,132 @@@ fail : STORE_REF_ERROR_OTHER; } -#define REFCOL_WIDTH 10 +static int refcol_width = 10; +static int compact_format; + +static void adjust_refcol_width(const struct ref *ref) +{ + int max, rlen, llen, len; + + /* uptodate lines are only shown on high verbosity level */ + if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid)) + return; + + max = term_columns(); + rlen = utf8_strwidth(prettify_refname(ref->name)); + + llen = utf8_strwidth(prettify_refname(ref->peer_ref->name)); + + /* + * rough estimation to see if the output line is too long and + * should not be counted (we can't do precise calculation + * anyway because we don't know if the error explanation part + * will be printed in update_local_ref) + */ + if (compact_format) { + llen = 0; + max = max * 2 / 3; + } + len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen; + if (len >= max) + return; + + /* + * Not precise calculation for compact mode because '*' can + * appear on the left hand side of '->' and shrink the column + * back. + */ + if (refcol_width < rlen) + refcol_width = rlen; +} + +static void prepare_format_display(struct ref *ref_map) +{ + struct ref *rm; + const char *format = "full"; + + git_config_get_string_const("fetch.output", &format); + if (!strcasecmp(format, "full")) + compact_format = 0; + else if (!strcasecmp(format, "compact")) + compact_format = 1; + else + die(_("configuration fetch.output contains invalid value %s"), + format); + + for (rm = ref_map; rm; rm = rm->next) { + if (rm->status == REF_STATUS_REJECT_SHALLOW || + !rm->peer_ref || + !strcmp(rm->name, "HEAD")) + continue; + + adjust_refcol_width(rm); + } +} + +static void print_remote_to_local(struct strbuf *display, + const char *remote, const char *local) +{ + strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local); +} + +static int find_and_replace(struct strbuf *haystack, + const char *needle, + const char *placeholder) +{ + const char *p = strstr(haystack->buf, needle); + int plen, nlen; + + if (!p) + return 0; + + if (p > haystack->buf && p[-1] != '/') + return 0; + + plen = strlen(p); + nlen = strlen(needle); + if (plen > nlen && p[nlen] != '/') + return 0; + + strbuf_splice(haystack, p - haystack->buf, nlen, + placeholder, strlen(placeholder)); + return 1; +} + +static void print_compact(struct strbuf *display, + const char *remote, const char *local) +{ + struct strbuf r = STRBUF_INIT; + struct strbuf l = STRBUF_INIT; + + if (!strcmp(remote, local)) { + strbuf_addf(display, "%-*s -> *", refcol_width, remote); + return; + } + + strbuf_addstr(&r, remote); + strbuf_addstr(&l, local); + + if (!find_and_replace(&r, local, "*")) + find_and_replace(&l, remote, "*"); + print_remote_to_local(display, r.buf, l.buf); + + strbuf_release(&r); + strbuf_release(&l); +} + +static void format_display(struct strbuf *display, char code, + const char *summary, const char *error, + const char *remote, const char *local) +{ + strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary)); + if (!compact_format) + print_remote_to_local(display, remote, local); + else + print_compact(display, remote, local); + if (error) + strbuf_addf(display, " (%s)", error); +} static int update_local_ref(struct ref *ref, const char *remote, @@@ -593,8 -467,9 +593,8 @@@ if (!oidcmp(&ref->old_oid, &ref->new_oid)) { if (verbosity > 0) - strbuf_addf(display, "= %-*s %-*s -> %s", - TRANSPORT_SUMMARY(_("[up to date]")), - REFCOL_WIDTH, remote, pretty_ref); + format_display(display, '=', _("[up to date]"), NULL, + remote, pretty_ref); return 0; } @@@ -606,9 -481,10 +606,9 @@@ * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - strbuf_addf(display, - _("! %-*s %-*s -> %s (can't fetch in current branch)"), - TRANSPORT_SUMMARY(_("[rejected]")), - REFCOL_WIDTH, remote, pretty_ref); + format_display(display, '!', _("[rejected]"), + _("can't fetch in current branch"), + remote, pretty_ref); return 1; } @@@ -616,9 -492,11 +616,9 @@@ starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : '-', - TRANSPORT_SUMMARY(_("[tag update]")), - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : 't', _("[tag update]"), + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); return r; } @@@ -649,9 -527,11 +649,9 @@@ (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref(msg, ref, 0); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : '*', - TRANSPORT_SUMMARY(what), - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : '*', what, + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); return r; } @@@ -665,9 -545,11 +665,9 @@@ (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref("fast-forward", ref, 1); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : ' ', - TRANSPORT_SUMMARY_WIDTH, quickref.buf, - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : ' ', quickref.buf, + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); strbuf_release(&quickref); return r; } else if (force || ref->force) { @@@ -680,14 -562,18 +680,14 @@@ (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref("forced-update", ref, 1); - strbuf_addf(display, "%c %-*s %-*s -> %s (%s)", - r ? '!' : '+', - TRANSPORT_SUMMARY_WIDTH, quickref.buf, - REFCOL_WIDTH, remote, pretty_ref, - r ? _("unable to update local ref") : _("forced update")); + format_display(display, r ? '!' : '+', quickref.buf, + r ? _("unable to update local ref") : _("forced update"), + remote, pretty_ref); strbuf_release(&quickref); return r; } else { - strbuf_addf(display, "! %-*s %-*s -> %s %s", - TRANSPORT_SUMMARY(_("[rejected]")), - REFCOL_WIDTH, remote, pretty_ref, - _("(non-fast-forward)")); + format_display(display, '!', _("[rejected]"), _("non-fast-forward"), + remote, pretty_ref); return 1; } } @@@ -721,7 -607,7 +721,7 @@@ static int store_updated_refs(const cha fp = fopen(filename, "a"); if (!fp) - return error(_("cannot open %s: %s\n"), filename, strerror(errno)); + return error_errno(_("cannot open %s"), filename); if (raw_url) url = transport_anonymize_url(raw_url); @@@ -734,8 -620,6 +734,8 @@@ goto abort; } + prepare_format_display(ref_map); + /* * We do a pass for each fetch_head_status type in their enum order, so * merged entries are written before not-for-merge. That lets readers @@@ -830,10 -714,11 +830,10 @@@ rc |= update_local_ref(ref, what, rm, ¬e); free(ref); } else - strbuf_addf(¬e, "* %-*s %-*s -> FETCH_HEAD", - TRANSPORT_SUMMARY_WIDTH, - *kind ? kind : "branch", - REFCOL_WIDTH, - *what ? what : "HEAD"); + format_display(¬e, '*', + *kind ? kind : "branch", NULL, + *what ? what : "HEAD", + "FETCH_HEAD"); if (note.len) { if (verbosity >= 0 && !shown_url) { fprintf(stderr, _("From %.*s\n"), @@@ -921,21 -806,19 +921,21 @@@ static int prune_refs(struct refspec *r for (ref = stale_refs; ref; ref = ref->next) string_list_append(&refnames, ref->name); - result = delete_refs(&refnames); + result = delete_refs(&refnames, 0); string_list_clear(&refnames, 0); } if (verbosity >= 0) { for (ref = stale_refs; ref; ref = ref->next) { + struct strbuf sb = STRBUF_INIT; if (!shown_url) { fprintf(stderr, _("From %.*s\n"), url_len, url); shown_url = 1; } - fprintf(stderr, " x %-*s %-*s -> %s\n", - TRANSPORT_SUMMARY(_("[deleted]")), - REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name)); + format_display(&sb, '-', _("[deleted]"), NULL, + _("(none)"), prettify_refname(ref->name)); + fprintf(stderr, " %s\n",sb.buf); + strbuf_release(&sb); warn_dangling_symref(stderr, dangling_msg, ref->name); } } @@@ -965,7 -848,7 +965,7 @@@ static int truncate_fetch_head(void FILE *fp = fopen_for_writing(filename); if (!fp) - return error(_("cannot open %s: %s\n"), filename, strerror(errno)); + return error_errno(_("cannot open %s"), filename); fclose(fp); return 0; } @@@ -1122,7 -1005,7 +1122,7 @@@ static int get_remote_group(const char size_t wordlen = strcspn(value, " \t\n"); if (wordlen >= 1) - string_list_append(g->list, + string_list_append_nodup(g->list, xstrndup(value, wordlen)); value += wordlen + (value[wordlen] != '\0'); } @@@ -1260,7 -1143,7 +1260,7 @@@ static int fetch_one(struct remote *rem int cmd_fetch(int argc, const char **argv, const char *prefix) { int i; - struct string_list list = STRING_LIST_INIT_NODUP; + struct string_list list = STRING_LIST_INIT_DUP; struct remote *remote; int result = 0; struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; @@@ -1343,6 -1226,8 +1343,6 @@@ argv_array_clear(&options); } - /* All names were strdup()ed or strndup()ed */ - list.strdup_strings = 1; string_list_clear(&list, 0); close_all_packs(); diff --combined builtin/remote.c index a4d9c1a8e9,c4b4d674bd..9f6a6b3a9c --- a/builtin/remote.c +++ b/builtin/remote.c @@@ -247,7 -247,7 +247,7 @@@ struct branch_info enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase; }; -static struct string_list branch_list; +static struct string_list branch_list = STRING_LIST_INIT_NODUP; static const char *abbrev_ref(const char *name, const char *prefix) { @@@ -539,10 -539,6 +539,6 @@@ static int add_branch_for_removal(cons return 0; } - /* make sure that symrefs are deleted */ - if (flags & REF_ISSYMREF) - return unlink(git_path("%s", refname)); - string_list_append(branches->branches, refname); return 0; @@@ -788,7 -784,7 +784,7 @@@ static int rm(int argc, const char **ar strbuf_release(&buf); if (!result) - result = delete_refs(&branches); + result = delete_refs(&branches, REF_NODEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@@ -952,7 -948,7 +948,7 @@@ static int show_local_info_item(struct struct show_info *show_info = cb_data; struct branch_info *branch_info = item->util; struct string_list *merge = &branch_info->merge; - const char *also; + int width = show_info->width + 4; int i; if (branch_info->rebase && branch_info->merge.nr > 1) { @@@ -963,18 -959,19 +959,18 @@@ printf(" %-*s ", show_info->width, item->string); if (branch_info->rebase) { - printf_ln(_(branch_info->rebase == INTERACTIVE_REBASE ? - "rebases interactively onto remote %s" : - "rebases onto remote %s"), merge->items[0].string); + printf_ln(branch_info->rebase == INTERACTIVE_REBASE + ? _("rebases interactively onto remote %s") + : _("rebases onto remote %s"), merge->items[0].string); return 0; } else if (show_info->any_rebase) { printf_ln(_(" merges with remote %s"), merge->items[0].string); - also = _(" and with remote"); + width++; } else { printf_ln(_("merges with remote %s"), merge->items[0].string); - also = _(" and with remote"); } for (i = 1; i < merge->nr; i++) - printf(" %-*s %s %s\n", show_info->width, "", also, + printf(_("%-*s and with remote %s\n"), width, "", merge->items[i].string); return 0; @@@ -1153,15 -1150,13 +1149,15 @@@ static int show(int argc, const char ** url_nr = states.remote->url_nr; } for (i = 0; i < url_nr; i++) + /* TRANSLATORS: the colon ':' should align with + the one in " Fetch URL: %s" translation */ printf_ln(_(" Push URL: %s"), url[i]); if (!i) - printf_ln(_(" Push URL: %s"), "(no URL)"); + printf_ln(_(" Push URL: %s"), _("(no URL)")); if (no_query) - printf_ln(_(" HEAD branch: %s"), "(not queried)"); + printf_ln(_(" HEAD branch: %s"), _("(not queried)")); else if (!states.heads.nr) - printf_ln(_(" HEAD branch: %s"), "(unknown)"); + printf_ln(_(" HEAD branch: %s"), _("(unknown)")); else if (states.heads.nr == 1) printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string); else { @@@ -1304,7 -1299,7 +1300,7 @@@ static int prune_remote(const char *rem string_list_sort(&refs_to_prune); if (!dry_run) - result |= delete_refs(&refs_to_prune); + result |= delete_refs(&refs_to_prune, 0); for_each_string_list_item(item, &states.stale) { const char *refname = item->util; diff --combined refs.h index 56089d5724,442c1a5db8..1b02043758 --- a/refs.h +++ b/refs.h @@@ -52,19 -52,19 +52,19 @@@ #define RESOLVE_REF_NO_RECURSE 0x02 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04 - extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); + const char *resolve_ref_unsafe(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); - extern char *resolve_refdup(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); + char *resolve_refdup(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); - extern int read_ref_full(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); - extern int read_ref(const char *refname, unsigned char *sha1); + int read_ref_full(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); + int read_ref(const char *refname, unsigned char *sha1); - extern int ref_exists(const char *refname); + int ref_exists(const char *refname); - extern int is_branch(const char *refname); + int is_branch(const char *refname); /* * If refname is a non-symbolic reference that refers to a tag object, @@@ -74,24 -74,25 +74,25 @@@ * Symbolic references are considered unpeelable, even if they * ultimately resolve to a peelable tag. */ - extern int peel_ref(const char *refname, unsigned char *sha1); + int peel_ref(const char *refname, unsigned char *sha1); /** * Resolve refname in the nested "gitlink" repository that is located * at path. If the resolution is successful, return 0 and set sha1 to * the name of the object; otherwise, return a non-zero value. */ - extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1); + int resolve_gitlink_ref(const char *path, const char *refname, + unsigned char *sha1); /* * Return true iff abbrev_name is a possible abbreviation for * full_name according to the rules defined by ref_rev_parse_rules in * refs.c. */ - extern int refname_match(const char *abbrev_name, const char *full_name); + int refname_match(const char *abbrev_name, const char *full_name); - extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); - extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); + int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); + int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); /* * A ref_transaction represents a collection of ref updates @@@ -140,7 -141,9 +141,9 @@@ struct ref_transaction; /* - * Bit values set in the flags argument passed to each_ref_fn(): + * Bit values set in the flags argument passed to each_ref_fn() and + * stored in ref_iterator::flags. Other bits are for internal use + * only: */ /* Reference is a symbolic reference. */ @@@ -182,38 -185,45 +185,45 @@@ typedef int each_ref_fn(const char *ref * modifies the reference also returns a nonzero value to immediately * stop the iteration. */ - extern int head_ref(each_ref_fn fn, void *cb_data); - extern int for_each_ref(each_ref_fn fn, void *cb_data); - extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); - extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken); - extern int for_each_tag_ref(each_ref_fn fn, void *cb_data); - extern int for_each_branch_ref(each_ref_fn fn, void *cb_data); - extern int for_each_remote_ref(each_ref_fn fn, void *cb_data); - extern int for_each_replace_ref(each_ref_fn fn, void *cb_data); - extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); - extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data); - - extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); - extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); - extern int for_each_ref_in_submodule(const char *submodule, const char *prefix, + int head_ref(each_ref_fn fn, void *cb_data); + int for_each_ref(each_ref_fn fn, void *cb_data); + int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); + int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, + unsigned int broken); + int for_each_tag_ref(each_ref_fn fn, void *cb_data); + int for_each_branch_ref(each_ref_fn fn, void *cb_data); + int for_each_remote_ref(each_ref_fn fn, void *cb_data); + int for_each_replace_ref(each_ref_fn fn, void *cb_data); + int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); + int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, + const char *prefix, void *cb_data); + + int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); + int for_each_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); + int for_each_ref_in_submodule(const char *submodule, const char *prefix, each_ref_fn fn, void *cb_data); - extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); - extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); - extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); + int for_each_tag_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); + int for_each_branch_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); + int for_each_remote_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); - extern int head_ref_namespaced(each_ref_fn fn, void *cb_data); - extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); + int head_ref_namespaced(each_ref_fn fn, void *cb_data); + int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); /* can be used to learn about broken ref and symref */ - extern int for_each_rawref(each_ref_fn fn, void *cb_data); + int for_each_rawref(each_ref_fn fn, void *cb_data); static inline const char *has_glob_specials(const char *pattern) { return strpbrk(pattern, "?*["); } - extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); - extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames); + void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); + void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, + const struct string_list *refnames); /* * Flags for controlling behaviour of pack_refs() @@@ -245,13 -255,13 +255,13 @@@ int pack_refs(unsigned int flags) int safe_create_reflog(const char *refname, int force_create, struct strbuf *err); /** Reads log for the value of ref during at_time. **/ - extern int read_ref_at(const char *refname, unsigned int flags, - unsigned long at_time, int cnt, - unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); + int read_ref_at(const char *refname, unsigned int flags, + unsigned long at_time, int cnt, + unsigned char *sha1, char **msg, + unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /** Check if a particular reflog exists */ - extern int reflog_exists(const char *refname); + int reflog_exists(const char *refname); /* * Delete the specified reference. If old_sha1 is non-NULL, then @@@ -260,21 -270,26 +270,26 @@@ * exists, regardless of its old value. It is an error for old_sha1 to * be NULL_SHA1. flags is passed through to ref_transaction_delete(). */ - extern int delete_ref(const char *refname, const unsigned char *old_sha1, - unsigned int flags); + int delete_ref(const char *refname, const unsigned char *old_sha1, + unsigned int flags); /* * Delete the specified references. If there are any problems, emit * errors but attempt to keep going (i.e., the deletes are not done in - * an all-or-nothing transaction). + * an all-or-nothing transaction). flags is passed through to + * ref_transaction_delete(). */ - extern int delete_refs(struct string_list *refnames); + int delete_refs(struct string_list *refnames, unsigned int flags); /** Delete a reflog */ - extern int delete_reflog(const char *refname); + int delete_reflog(const char *refname); /* iterate over reflog entries */ - typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); + typedef int each_reflog_ent_fn( + unsigned char *old_sha1, unsigned char *new_sha1, + const char *committer, unsigned long timestamp, + int tz, const char *msg, void *cb_data); + int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data); int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); @@@ -282,7 -297,7 +297,7 @@@ * Calls the specified function for each reflog file until it returns nonzero, * and returns the value */ - extern int for_each_reflog(each_ref_fn, void *); + int for_each_reflog(each_ref_fn fn, void *cb_data); #define REFNAME_ALLOW_ONELEVEL 1 #define REFNAME_REFSPEC_PATTERN 2 @@@ -295,16 -310,16 +310,16 @@@ * allow a single "*" wildcard character in the refspec. No leading or * repeated slashes are accepted. */ - extern int check_refname_format(const char *refname, int flags); + int check_refname_format(const char *refname, int flags); - extern const char *prettify_refname(const char *refname); + const char *prettify_refname(const char *refname); - extern char *shorten_unambiguous_ref(const char *refname, int strict); + char *shorten_unambiguous_ref(const char *refname, int strict); /** rename ref, return 0 on success **/ - extern int rename_ref(const char *oldref, const char *newref, const char *logmsg); + int rename_ref(const char *oldref, const char *newref, const char *logmsg); - extern int create_symref(const char *refname, const char *target, const char *logmsg); + int create_symref(const char *refname, const char *target, const char *logmsg); /* * Update HEAD of the specified gitdir. @@@ -313,7 -328,7 +328,7 @@@ * $GIT_DIR points to. * Return 0 if successful, non-zero otherwise. * */ - extern int set_worktree_head_symref(const char *gitdir, const char *target); + int set_worktree_head_symref(const char *gitdir, const char *target); enum action_on_err { UPDATE_REFS_MSG_ON_ERR, @@@ -345,7 -360,7 +360,7 @@@ struct ref_transaction *ref_transaction * msg -- a message describing the change (for the reflog). * * err -- a strbuf for receiving a description of any error that - * might have occured. + * might have occurred. * * The functions make internal copies of refname and msg, so the * caller retains ownership of these parameters. @@@ -463,7 -478,7 +478,7 @@@ int update_ref(const char *msg, const c const unsigned char *new_sha1, const unsigned char *old_sha1, unsigned int flags, enum action_on_err onerr); - extern int parse_hide_refs_config(const char *var, const char *value, const char *); + int parse_hide_refs_config(const char *var, const char *value, const char *); /* * Check whether a ref is hidden. If no namespace is set, both the first and @@@ -473,7 -488,7 +488,7 @@@ * the ref is outside that namespace, the first parameter is NULL. The second * parameter always points to the full ref name. */ - extern int ref_is_hidden(const char *, const char *); + int ref_is_hidden(const char *, const char *); enum ref_type { REF_TYPE_PER_WORKTREE, @@@ -522,11 -537,11 +537,11 @@@ typedef void reflog_expiry_cleanup_fn(v * enum expire_reflog_flags. The three function pointers are described * above. On success, return zero. */ - extern int reflog_expire(const char *refname, const unsigned char *sha1, - unsigned int flags, - reflog_expiry_prepare_fn prepare_fn, - reflog_expiry_should_prune_fn should_prune_fn, - reflog_expiry_cleanup_fn cleanup_fn, - void *policy_cb_data); + int reflog_expire(const char *refname, const unsigned char *sha1, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data); #endif /* REFS_H */ diff --combined refs/files-backend.c index 1bf643025c,a9ca003bae..12290d2496 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@@ -1,6 -1,8 +1,8 @@@ #include "../cache.h" #include "../refs.h" #include "refs-internal.h" + #include "../iterator.h" + #include "../dir-iterator.h" #include "../lockfile.h" #include "../object.h" #include "../dir.h" @@@ -513,68 -515,36 +515,36 @@@ static void sort_ref_dir(struct ref_di } /* - * Return true iff the reference described by entry can be resolved to - * an object in the database. Emit a warning if the referred-to - * object does not exist. + * Return true if refname, which has the specified oid and flags, can + * be resolved to an object in the database. If the referred-to object + * does not exist, emit a warning and return false. */ - static int ref_resolves_to_object(struct ref_entry *entry) + static int ref_resolves_to_object(const char *refname, + const struct object_id *oid, + unsigned int flags) { - if (entry->flag & REF_ISBROKEN) + if (flags & REF_ISBROKEN) return 0; - if (!has_sha1_file(entry->u.value.oid.hash)) { - error("%s does not point to a valid object!", entry->name); + if (!has_sha1_file(oid->hash)) { + error("%s does not point to a valid object!", refname); return 0; } return 1; } /* - * current_ref is a performance hack: when iterating over references - * using the for_each_ref*() functions, current_ref is set to the - * current reference's entry before calling the callback function. If - * the callback function calls peel_ref(), then peel_ref() first - * checks whether the reference to be peeled is the current reference - * (it usually is) and if so, returns that reference's peeled version - * if it is available. This avoids a refname lookup in a common case. + * Return true if the reference described by entry can be resolved to + * an object in the database; otherwise, emit a warning and return + * false. */ - static struct ref_entry *current_ref; - - typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); - - struct ref_entry_cb { - const char *base; - int trim; - int flags; - each_ref_fn *fn; - void *cb_data; - }; - - /* - * Handle one reference in a do_for_each_ref*()-style iteration, - * calling an each_ref_fn for each entry. - */ - static int do_one_ref(struct ref_entry *entry, void *cb_data) + static int entry_resolves_to_object(struct ref_entry *entry) { - struct ref_entry_cb *data = cb_data; - struct ref_entry *old_current_ref; - int retval; - - if (!starts_with(entry->name, data->base)) - return 0; - - if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) && - !ref_resolves_to_object(entry)) - return 0; - - /* Store the old value, in case this is a recursive call: */ - old_current_ref = current_ref; - current_ref = entry; - retval = data->fn(entry->name + data->trim, &entry->u.value.oid, - entry->flag, data->cb_data); - current_ref = old_current_ref; - return retval; + return ref_resolves_to_object(entry->name, + &entry->u.value.oid, entry->flag); } + typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); + /* * Call fn for each reference in dir that has index in the range * offset <= index < dir->nr. Recurse into subdirectories that are in @@@ -603,78 -573,6 +573,6 @@@ static int do_for_each_entry_in_dir(str return 0; } - /* - * Call fn for each reference in the union of dir1 and dir2, in order - * by refname. Recurse into subdirectories. If a value entry appears - * in both dir1 and dir2, then only process the version that is in - * dir2. The input dirs must already be sorted, but subdirs will be - * sorted as needed. fn is called for all references, including - * broken ones. - */ - static int do_for_each_entry_in_dirs(struct ref_dir *dir1, - struct ref_dir *dir2, - each_ref_entry_fn fn, void *cb_data) - { - int retval; - int i1 = 0, i2 = 0; - - assert(dir1->sorted == dir1->nr); - assert(dir2->sorted == dir2->nr); - while (1) { - struct ref_entry *e1, *e2; - int cmp; - if (i1 == dir1->nr) { - return do_for_each_entry_in_dir(dir2, i2, fn, cb_data); - } - if (i2 == dir2->nr) { - return do_for_each_entry_in_dir(dir1, i1, fn, cb_data); - } - e1 = dir1->entries[i1]; - e2 = dir2->entries[i2]; - cmp = strcmp(e1->name, e2->name); - if (cmp == 0) { - if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) { - /* Both are directories; descend them in parallel. */ - struct ref_dir *subdir1 = get_ref_dir(e1); - struct ref_dir *subdir2 = get_ref_dir(e2); - sort_ref_dir(subdir1); - sort_ref_dir(subdir2); - retval = do_for_each_entry_in_dirs( - subdir1, subdir2, fn, cb_data); - i1++; - i2++; - } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) { - /* Both are references; ignore the one from dir1. */ - retval = fn(e2, cb_data); - i1++; - i2++; - } else { - die("conflict between reference and directory: %s", - e1->name); - } - } else { - struct ref_entry *e; - if (cmp < 0) { - e = e1; - i1++; - } else { - e = e2; - i2++; - } - if (e->flag & REF_DIR) { - struct ref_dir *subdir = get_ref_dir(e); - sort_ref_dir(subdir); - retval = do_for_each_entry_in_dir( - subdir, 0, fn, cb_data); - } else { - retval = fn(e, cb_data); - } - } - if (retval) - return retval; - } - } - /* * Load all of the refs from the dir into our in-memory cache. The hard work * of loading loose refs is done by get_ref_dir(), so we just need to recurse @@@ -691,6 -589,153 +589,153 @@@ static void prime_ref_dir(struct ref_di } } + /* + * A level in the reference hierarchy that is currently being iterated + * through. + */ + struct cache_ref_iterator_level { + /* + * The ref_dir being iterated over at this level. The ref_dir + * is sorted before being stored here. + */ + struct ref_dir *dir; + + /* + * The index of the current entry within dir (which might + * itself be a directory). If index == -1, then the iteration + * hasn't yet begun. If index == dir->nr, then the iteration + * through this level is over. + */ + int index; + }; + + /* + * Represent an iteration through a ref_dir in the memory cache. The + * iteration recurses through subdirectories. + */ + struct cache_ref_iterator { + struct ref_iterator base; + + /* + * The number of levels currently on the stack. This is always + * at least 1, because when it becomes zero the iteration is + * ended and this struct is freed. + */ + size_t levels_nr; + + /* The number of levels that have been allocated on the stack */ + size_t levels_alloc; + + /* + * A stack of levels. levels[0] is the uppermost level that is + * being iterated over in this iteration. (This is not + * necessary the top level in the references hierarchy. If we + * are iterating through a subtree, then levels[0] will hold + * the ref_dir for that subtree, and subsequent levels will go + * on from there.) + */ + struct cache_ref_iterator_level *levels; + }; + + static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) + { + struct cache_ref_iterator *iter = + (struct cache_ref_iterator *)ref_iterator; + + while (1) { + struct cache_ref_iterator_level *level = + &iter->levels[iter->levels_nr - 1]; + struct ref_dir *dir = level->dir; + struct ref_entry *entry; + + if (level->index == -1) + sort_ref_dir(dir); + + if (++level->index == level->dir->nr) { + /* This level is exhausted; pop up a level */ + if (--iter->levels_nr == 0) + return ref_iterator_abort(ref_iterator); + + continue; + } + + entry = dir->entries[level->index]; + + if (entry->flag & REF_DIR) { + /* push down a level */ + ALLOC_GROW(iter->levels, iter->levels_nr + 1, + iter->levels_alloc); + + level = &iter->levels[iter->levels_nr++]; + level->dir = get_ref_dir(entry); + level->index = -1; + } else { + iter->base.refname = entry->name; + iter->base.oid = &entry->u.value.oid; + iter->base.flags = entry->flag; + return ITER_OK; + } + } + } + + static enum peel_status peel_entry(struct ref_entry *entry, int repeel); + + static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) + { + struct cache_ref_iterator *iter = + (struct cache_ref_iterator *)ref_iterator; + struct cache_ref_iterator_level *level; + struct ref_entry *entry; + + level = &iter->levels[iter->levels_nr - 1]; + + if (level->index == -1) + die("BUG: peel called before advance for cache iterator"); + + entry = level->dir->entries[level->index]; + + if (peel_entry(entry, 0)) + return -1; + hashcpy(peeled->hash, entry->u.value.peeled.hash); + return 0; + } + + static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) + { + struct cache_ref_iterator *iter = + (struct cache_ref_iterator *)ref_iterator; + + free(iter->levels); + base_ref_iterator_free(ref_iterator); + return ITER_DONE; + } + + static struct ref_iterator_vtable cache_ref_iterator_vtable = { + cache_ref_iterator_advance, + cache_ref_iterator_peel, + cache_ref_iterator_abort + }; + + static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir) + { + struct cache_ref_iterator *iter; + struct ref_iterator *ref_iterator; + struct cache_ref_iterator_level *level; + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable); + ALLOC_GROW(iter->levels, 10, iter->levels_alloc); + + iter->levels_nr = 1; + level = &iter->levels[0]; + level->index = -1; + level->dir = dir; + + return ref_iterator; + } + struct nonmatching_ref_data { const struct string_list *skip; const char *conflicting_refname; @@@ -954,15 -999,26 +999,26 @@@ static struct ref_cache *lookup_ref_cac /* * Return a pointer to a ref_cache for the specified submodule. For - * the main repository, use submodule==NULL. The returned structure - * will be allocated and initialized but not necessarily populated; it - * should not be freed. + * the main repository, use submodule==NULL; such a call cannot fail. + * For a submodule, the submodule must exist and be a nonbare + * repository, otherwise return NULL. + * + * The returned structure will be allocated and initialized but not + * necessarily populated; it should not be freed. */ static struct ref_cache *get_ref_cache(const char *submodule) { struct ref_cache *refs = lookup_ref_cache(submodule); - if (!refs) - refs = create_ref_cache(submodule); + + if (!refs) { + struct strbuf submodule_sb = STRBUF_INIT; + + strbuf_addstr(&submodule_sb, submodule); + if (is_nonbare_repository_dir(&submodule_sb)) + refs = create_ref_cache(submodule); + strbuf_release(&submodule_sb); + } + return refs; } @@@ -1341,13 -1397,10 +1397,10 @@@ int resolve_gitlink_ref(const char *pat return -1; strbuf_add(&submodule, path, len); - refs = lookup_ref_cache(submodule.buf); + refs = get_ref_cache(submodule.buf); if (!refs) { - if (!is_nonbare_repository_dir(&submodule)) { - strbuf_release(&submodule); - return -1; - } - refs = create_ref_cache(submodule.buf); + strbuf_release(&submodule); + return -1; } strbuf_release(&submodule); @@@ -1790,11 -1843,12 +1843,12 @@@ int peel_ref(const char *refname, unsig int flag; unsigned char base[20]; - if (current_ref && (current_ref->name == refname - || !strcmp(current_ref->name, refname))) { - if (peel_entry(current_ref, 0)) + if (current_ref_iter && current_ref_iter->refname == refname) { + struct object_id peeled; + + if (ref_iterator_peel(current_ref_iter, &peeled)) return -1; - hashcpy(sha1, current_ref->u.value.peeled.hash); + hashcpy(sha1, peeled.hash); return 0; } @@@ -1822,90 -1876,137 +1876,137 @@@ return peel_object(base, sha1); } - /* - * Call fn for each reference in the specified ref_cache, omitting - * references not in the containing_dir of base. fn is called for all - * references, including broken ones. If fn ever returns a non-zero - * value, stop the iteration and return that value; otherwise, return - * 0. - */ - static int do_for_each_entry(struct ref_cache *refs, const char *base, - each_ref_entry_fn fn, void *cb_data) - { + struct files_ref_iterator { + struct ref_iterator base; + struct packed_ref_cache *packed_ref_cache; - struct ref_dir *loose_dir; - struct ref_dir *packed_dir; - int retval = 0; + struct ref_iterator *iter0; + unsigned int flags; + }; - /* - * We must make sure that all loose refs are read before accessing the - * packed-refs file; this avoids a race condition in which loose refs - * are migrated to the packed-refs file by a simultaneous process, but - * our in-memory view is from before the migration. get_packed_ref_cache() - * takes care of making sure our view is up to date with what is on - * disk. - */ - loose_dir = get_loose_refs(refs); - if (base && *base) { - loose_dir = find_containing_dir(loose_dir, base, 0); - } - if (loose_dir) - prime_ref_dir(loose_dir); + static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) + { + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; + int ok; - packed_ref_cache = get_packed_ref_cache(refs); - acquire_packed_ref_cache(packed_ref_cache); - packed_dir = get_packed_ref_dir(packed_ref_cache); - if (base && *base) { - packed_dir = find_containing_dir(packed_dir, base, 0); - } - - if (packed_dir && loose_dir) { - sort_ref_dir(packed_dir); - sort_ref_dir(loose_dir); - retval = do_for_each_entry_in_dirs( - packed_dir, loose_dir, fn, cb_data); - } else if (packed_dir) { - sort_ref_dir(packed_dir); - retval = do_for_each_entry_in_dir( - packed_dir, 0, fn, cb_data); - } else if (loose_dir) { - sort_ref_dir(loose_dir); - retval = do_for_each_entry_in_dir( - loose_dir, 0, fn, cb_data); + while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) { + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && + !ref_resolves_to_object(iter->iter0->refname, + iter->iter0->oid, + iter->iter0->flags)) + continue; + + iter->base.refname = iter->iter0->refname; + iter->base.oid = iter->iter0->oid; + iter->base.flags = iter->iter0->flags; + return ITER_OK; } - release_packed_ref_cache(packed_ref_cache); - return retval; + iter->iter0 = NULL; + if (ref_iterator_abort(ref_iterator) != ITER_DONE) + ok = ITER_ERROR; + + return ok; } - /* - * Call fn for each reference in the specified ref_cache for which the - * refname begins with base. If trim is non-zero, then trim that many - * characters off the beginning of each refname before passing the - * refname to fn. flags can be DO_FOR_EACH_INCLUDE_BROKEN to include - * broken references in the iteration. If fn ever returns a non-zero - * value, stop the iteration and return that value; otherwise, return - * 0. - */ - int do_for_each_ref(const char *submodule, const char *base, - each_ref_fn fn, int trim, int flags, void *cb_data) + static int files_ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) { - struct ref_entry_cb data; - struct ref_cache *refs; + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; - refs = get_ref_cache(submodule); - data.base = base; - data.trim = trim; - data.flags = flags; - data.fn = fn; - data.cb_data = cb_data; + return ref_iterator_peel(iter->iter0, peeled); + } + + static int files_ref_iterator_abort(struct ref_iterator *ref_iterator) + { + struct files_ref_iterator *iter = + (struct files_ref_iterator *)ref_iterator; + int ok = ITER_DONE; + + if (iter->iter0) + ok = ref_iterator_abort(iter->iter0); + + release_packed_ref_cache(iter->packed_ref_cache); + base_ref_iterator_free(ref_iterator); + return ok; + } + + static struct ref_iterator_vtable files_ref_iterator_vtable = { + files_ref_iterator_advance, + files_ref_iterator_peel, + files_ref_iterator_abort + }; + + struct ref_iterator *files_ref_iterator_begin( + const char *submodule, + const char *prefix, unsigned int flags) + { + struct ref_cache *refs = get_ref_cache(submodule); + struct ref_dir *loose_dir, *packed_dir; + struct ref_iterator *loose_iter, *packed_iter; + struct files_ref_iterator *iter; + struct ref_iterator *ref_iterator; + + if (!refs) + return empty_ref_iterator_begin(); if (ref_paranoia < 0) ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); if (ref_paranoia) - data.flags |= DO_FOR_EACH_INCLUDE_BROKEN; + flags |= DO_FOR_EACH_INCLUDE_BROKEN; + + iter = xcalloc(1, sizeof(*iter)); + ref_iterator = &iter->base; + base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable); + + /* + * We must make sure that all loose refs are read before + * accessing the packed-refs file; this avoids a race + * condition if loose refs are migrated to the packed-refs + * file by a simultaneous process, but our in-memory view is + * from before the migration. We ensure this as follows: + * First, we call prime_ref_dir(), which pre-reads the loose + * references for the subtree into the cache. (If they've + * already been read, that's OK; we only need to guarantee + * that they're read before the packed refs, not *how much* + * before.) After that, we call get_packed_ref_cache(), which + * internally checks whether the packed-ref cache is up to + * date with what is on disk, and re-reads it if not. + */ + + loose_dir = get_loose_refs(refs); + + if (prefix && *prefix) + loose_dir = find_containing_dir(loose_dir, prefix, 0); - return do_for_each_entry(refs, base, do_one_ref, &data); + if (loose_dir) { + prime_ref_dir(loose_dir); + loose_iter = cache_ref_iterator_begin(loose_dir); + } else { + /* There's nothing to iterate over. */ + loose_iter = empty_ref_iterator_begin(); + } + + iter->packed_ref_cache = get_packed_ref_cache(refs); + acquire_packed_ref_cache(iter->packed_ref_cache); + packed_dir = get_packed_ref_dir(iter->packed_ref_cache); + + if (prefix && *prefix) + packed_dir = find_containing_dir(packed_dir, prefix, 0); + + if (packed_dir) { + packed_iter = cache_ref_iterator_begin(packed_dir); + } else { + /* There's nothing to iterate over. */ + packed_iter = empty_ref_iterator_begin(); + } + + iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter); + iter->flags = flags; + + return ref_iterator; } /* @@@ -1929,14 -2030,14 +2030,14 @@@ static int verify_lock(struct ref_lock errno = save_errno; return -1; } else { - hashclr(lock->old_oid.hash); + oidclr(&lock->old_oid); return 0; } } if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) { strbuf_addf(err, "ref '%s' is at %s but expected %s", lock->ref_name, - sha1_to_hex(lock->old_oid.hash), + oid_to_hex(&lock->old_oid), sha1_to_hex(old_sha1)); errno = EBUSY; return -1; @@@ -2226,7 -2327,7 +2327,7 @@@ static int pack_if_possible_fn(struct r return 0; /* Do not pack symbolic or broken refs: */ - if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry)) + if ((entry->flag & REF_ISSYMREF) || !entry_resolves_to_object(entry)) return 0; /* Add a packed ref cache entry equivalent to the loose entry. */ @@@ -2412,7 -2513,7 +2513,7 @@@ static int delete_ref_loose(struct ref_ return 0; } - int delete_refs(struct string_list *refnames) + int delete_refs(struct string_list *refnames, unsigned int flags) { struct strbuf err = STRBUF_INIT; int i, result = 0; @@@ -2441,7 -2542,7 +2542,7 @@@ for (i = 0; i < refnames->nr; i++) { const char *refname = refnames->items[i].string; - if (delete_ref(refname, NULL, 0)) + if (delete_ref(refname, NULL, flags)) result |= error(_("could not remove reference %s"), refname); } @@@ -3191,60 -3292,88 +3292,88 @@@ int for_each_reflog_ent(const char *ref 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 - * space, but its contents will be restored before return. - */ - static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data) - { - DIR *d = opendir(git_path("logs/%s", name->buf)); - int retval = 0; - struct dirent *de; - int oldlen = name->len; - if (!d) - return name->len ? errno : 0; + struct files_reflog_iterator { + struct ref_iterator base; - while ((de = readdir(d)) != NULL) { - struct stat st; + struct dir_iterator *dir_iterator; + struct object_id oid; + }; - if (de->d_name[0] == '.') + static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) + { + struct files_reflog_iterator *iter = + (struct files_reflog_iterator *)ref_iterator; + struct dir_iterator *diter = iter->dir_iterator; + int ok; + + while ((ok = dir_iterator_advance(diter)) == ITER_OK) { + int flags; + + if (!S_ISREG(diter->st.st_mode)) continue; - if (ends_with(de->d_name, ".lock")) + if (diter->basename[0] == '.') + continue; + if (ends_with(diter->basename, ".lock")) continue; - strbuf_addstr(name, de->d_name); - if (stat(git_path("logs/%s", name->buf), &st) < 0) { - ; /* silently ignore */ - } else { - if (S_ISDIR(st.st_mode)) { - strbuf_addch(name, '/'); - retval = do_for_each_reflog(name, fn, cb_data); - } else { - struct object_id oid; - if (read_ref_full(name->buf, 0, oid.hash, NULL)) - retval = error("bad ref for %s", name->buf); - else - retval = fn(name->buf, &oid, 0, cb_data); - } - if (retval) - break; + if (read_ref_full(diter->relative_path, 0, + iter->oid.hash, &flags)) { + error("bad ref for %s", diter->path.buf); + continue; } - strbuf_setlen(name, oldlen); + + iter->base.refname = diter->relative_path; + iter->base.oid = &iter->oid; + iter->base.flags = flags; + return ITER_OK; } - closedir(d); - return retval; + + iter->dir_iterator = NULL; + if (ref_iterator_abort(ref_iterator) == ITER_ERROR) + ok = ITER_ERROR; + return ok; + } + + static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled) + { + die("BUG: ref_iterator_peel() called for reflog_iterator"); + } + + static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator) + { + struct files_reflog_iterator *iter = + (struct files_reflog_iterator *)ref_iterator; + int ok = ITER_DONE; + + if (iter->dir_iterator) + ok = dir_iterator_abort(iter->dir_iterator); + + base_ref_iterator_free(ref_iterator); + return ok; + } + + static struct ref_iterator_vtable files_reflog_iterator_vtable = { + files_reflog_iterator_advance, + files_reflog_iterator_peel, + files_reflog_iterator_abort + }; + + struct ref_iterator *files_reflog_iterator_begin(void) + { + struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter)); + struct ref_iterator *ref_iterator = &iter->base; + + base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); + iter->dir_iterator = dir_iterator_begin(git_path("logs")); + return ref_iterator; } int for_each_reflog(each_ref_fn fn, void *cb_data) { - int retval; - struct strbuf name; - strbuf_init(&name, PATH_MAX); - retval = do_for_each_reflog(&name, fn, cb_data); - strbuf_release(&name); - return retval; + return do_for_each_ref_iterator(files_reflog_iterator_begin(), + fn, cb_data); } static int ref_update_reject_duplicates(struct string_list *refnames, @@@ -3388,38 -3517,6 +3517,38 @@@ static const char *original_update_refn return update->refname; } +/* + * Check whether the REF_HAVE_OLD and old_oid values stored in update + * are consistent with oid, which is the reference's current value. If + * everything is OK, return 0; otherwise, write an error message to + * err and return -1. + */ +static int check_old_oid(struct ref_update *update, struct object_id *oid, + struct strbuf *err) +{ + if (!(update->flags & REF_HAVE_OLD) || + !hashcmp(oid->hash, update->old_sha1)) + return 0; + + if (is_null_sha1(update->old_sha1)) + strbuf_addf(err, "cannot lock ref '%s': " + "reference already exists", + original_update_refname(update)); + else if (is_null_oid(oid)) + strbuf_addf(err, "cannot lock ref '%s': " + "reference is missing but expected %s", + original_update_refname(update), + sha1_to_hex(update->old_sha1)); + else + strbuf_addf(err, "cannot lock ref '%s': " + "is at %s but expected %s", + original_update_refname(update), + oid_to_hex(oid), + sha1_to_hex(update->old_sha1)); + + return -1; +} + /* * Prepare for carrying out update: * - Lock the reference referred to by update. @@@ -3465,7 -3562,7 +3594,7 @@@ static int lock_ref_for_update(struct r reason = strbuf_detach(err, NULL); strbuf_addf(err, "cannot lock ref '%s': %s", - update->refname, reason); + original_update_refname(update), reason); free(reason); return ret; } @@@ -3479,17 -3576,28 +3608,17 @@@ * the transaction, so we have to read it here * to record and possibly check old_sha1: */ - if (read_ref_full(update->refname, - mustexist ? RESOLVE_REF_READING : 0, + if (read_ref_full(referent.buf, 0, lock->old_oid.hash, NULL)) { if (update->flags & REF_HAVE_OLD) { strbuf_addf(err, "cannot lock ref '%s': " - "can't resolve old value", - update->refname); - return TRANSACTION_GENERIC_ERROR; - } else { - hashclr(lock->old_oid.hash); + "error reading reference", + original_update_refname(update)); + return -1; } - } - if ((update->flags & REF_HAVE_OLD) && - hashcmp(lock->old_oid.hash, update->old_sha1)) { - strbuf_addf(err, "cannot lock ref '%s': " - "is at %s but expected %s", - update->refname, - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(update->old_sha1)); + } else if (check_old_oid(update, &lock->old_oid, err)) { return TRANSACTION_GENERIC_ERROR; } - } else { /* * Create a new update for the reference this @@@ -3506,9 -3614,6 +3635,9 @@@ } else { struct ref_update *parent_update; + if (check_old_oid(update, &lock->old_oid, err)) + return TRANSACTION_GENERIC_ERROR; + /* * If this update is happening indirectly because of a * symref update, record the old SHA-1 in the parent @@@ -3519,6 -3624,20 +3648,6 @@@ parent_update = parent_update->parent_update) { oidcpy(&parent_update->lock->old_oid, &lock->old_oid); } - - if ((update->flags & REF_HAVE_OLD) && - hashcmp(lock->old_oid.hash, update->old_sha1)) { - if (is_null_sha1(update->old_sha1)) - strbuf_addf(err, "cannot lock ref '%s': reference already exists", - original_update_refname(update)); - else - strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", - original_update_refname(update), - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(update->old_sha1)); - - return TRANSACTION_GENERIC_ERROR; - } } if ((update->flags & REF_HAVE_NEW) && @@@ -3540,7 -3659,7 +3669,7 @@@ */ update->lock = NULL; strbuf_addf(err, - "cannot update the ref '%s': %s", + "cannot update ref '%s': %s", update->refname, write_err); free(write_err); return TRANSACTION_GENERIC_ERROR;