diff options
122 files changed, 1609 insertions, 683 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 77346a4929..1fbdc2652b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -9,7 +9,7 @@ freebsd_task: DEFAULT_TEST_TARGET: prove DEVELOPER: 1 freebsd_instance: - image_family: freebsd-13-2 + image_family: freebsd-13-4 memory: 2G install_script: pkg install -y gettext gmake perl5 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80b1668ebe..4abfbc3e20 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,6 +9,8 @@ workflow: test:linux: image: $image + tags: + - saas-linux-medium-amd64 variables: CUSTOM_PATH: "/custom" before_script: diff --git a/Documentation/RelNotes/2.47.0.txt b/Documentation/RelNotes/2.47.0.txt index a6c1d4d78d..fbbea257a9 100644 --- a/Documentation/RelNotes/2.47.0.txt +++ b/Documentation/RelNotes/2.47.0.txt @@ -149,6 +149,19 @@ Performance, Internal Implementation, Development Support etc. been updated to pass the repository, if known, together with the prefix value. + * "git apply" had custom buffer management code that predated before + use of strbuf got widespread, which has been updated to use strbuf, + which also plugged some memory leaks. + + * The reftable backend learned to more efficiently handle exclude + patterns while enumerating the refs. + + * CI updates. FreeBSD image has been updated to 13.4. + (merge 2eeb29702e cb/ci-freebsd-13-4 later to maint). + + * Give timeout to the locking code to write to reftable, instead of + failing on the first failure without retrying. + Fixes since v2.46 ----------------- @@ -279,8 +292,35 @@ Fixes since v2.46 * A few usability fixes to "git jump" (in contrib/). (merge 083b82544d jk/jump-quickfix-fixes later to maint). + * "git diff --exit-code" ignored modified binary files, which has + been corrected. + (merge 9a41735af6 rs/diff-exit-code-binary later to maint). + + * When a subprocess to work in a submodule spawned by "git submodule" + fails with SIGPIPE, the parent Git process caught the death of it, + but gave a generic "failed to work in that submodule", which was + misleading. We now behave as if the parent got SIGPIPE and die. + (merge 082caf527e pw/submodule-process-sigpipe later to maint). + + * "git archive" with pathspec magic that uses the attribute + information did not work well, which has been corrected. + (merge 296743a7ca rs/archive-with-attr-pathspec-fix later to maint). + + * Background tasks "git maintenance" runs may need to use credential + information when going over the network, but a credential helper + may work only in an interactive environment, and end up blocking a + scheduled task waiting for UI. Credential helpers can now behave + differently when they are not running interactively. + (merge b9183b0a02 ds/background-maintenance-with-credential later to maint). + + * "git --git-dir=nowhere cmd" failed to properly notice that it + wasn't in any repository while processing includeIf.onbranch + configuration and instead crashed. + * Other code cleanup, docfix, build fix, etc. (merge be10ac7037 jc/mailinfo-header-cleanup later to maint). (merge 4460e052e0 jc/range-diff-lazy-setup later to maint). (merge 0627c58e7a ak/typofixes later to maint). (merge 83799f1500 jk/t9001-deflake later to maint). + (merge e02cc08a88 ak/typofix-2.46-maint later to maint). + (merge 5c5d29e1c4 ps/ci-gitlab-upgrade later to maint). diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt index 0221c3e620..470482ff4c 100644 --- a/Documentation/config/credential.txt +++ b/Documentation/config/credential.txt @@ -9,6 +9,14 @@ credential.helper:: Note that multiple helpers may be defined. See linkgit:gitcredentials[7] for details and examples. +credential.interactive:: + By default, Git and any configured credential helpers will ask for + user input when new credentials are required. Many of these helpers + will succeed based on stored credentials if those credentials are + still valid. To avoid the possibility of user interactivity from + Git, set `credential.interactive=false`. Some credential helpers + respect this option as well. + credential.useHttpPath:: When acquiring credentials, consider the "path" component of an http or https URL to be important. Defaults to false. See diff --git a/Documentation/config/reftable.txt b/Documentation/config/reftable.txt index 0515727977..57087803a5 100644 --- a/Documentation/config/reftable.txt +++ b/Documentation/config/reftable.txt @@ -46,3 +46,11 @@ reftable.geometricFactor:: By default, the geometric sequence uses a factor of 2, meaning that for any table, the next-biggest table must at least be twice as big. A maximum factor of 256 is supported. + +reftable.lockTimeout:: + Whenever the reftable backend appends a new table to the stack, it has + to lock the central "tables.list" file before updating it. This config + controls how long the process will wait to acquire the lock in case + another process has already acquired it. Value 0 means not to retry at + all; -1 means to try indefinitely. Default is 100 (i.e., retry for + 100ms). diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt index 6a869d67eb..5ffcfc9f2a 100644 --- a/Documentation/config/sendemail.txt +++ b/Documentation/config/sendemail.txt @@ -30,6 +30,21 @@ sendemail.confirm:: in the linkgit:git-send-email[1] documentation for the meaning of these values. +sendemail.mailmap:: + If true, makes linkgit:git-send-email[1] assume `--mailmap`, + otherwise assume `--no-mailmap`. False by default. + +sendemail.mailmap.file:: + The location of a linkgit:git-send-email[1] specific augmenting + mailmap file. The default mailmap and `mailmap.file` are loaded + first. Thus, entries in this file take precedence over entries in + the default mailmap locations. See linkgit:gitmailmap[5]. + +sendemail.mailmap.blob:: + Like `sendemail.mailmap.file`, but consider the value as a reference + to a blob in the repository. Entries in `sendemail.mailmap.file` + take precedence over entries here. See linkgit:gitmailmap[5]. + sendemail.aliasesFile:: To avoid typing long email addresses, point this to one or more email aliases files. You must also supply `sendemail.aliasFileType`. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 2e6f1d63ae..bc3ef45acb 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -414,6 +414,12 @@ exists when 'git send-email' is asked to add it (especially note that Failure to do so may not produce the expected result in the recipient's MUA. +--[no-]mailmap:: + Use the mailmap file (see linkgit:gitmailmap[5]) to map all + addresses to their canonical real name and email address. Additional + mailmap data specific to git-send-email may be provided using the + `sendemail.mailmap.file` or `sendemail.mailmap.blob` configuration + values. Defaults to `sendemail.mailmap`. Administering ~~~~~~~~~~~~~ diff --git a/Documentation/technical/sparse-checkout.txt b/Documentation/technical/sparse-checkout.txt index fa0d01cbda..d968659354 100644 --- a/Documentation/technical/sparse-checkout.txt +++ b/Documentation/technical/sparse-checkout.txt @@ -287,7 +287,7 @@ everything behaves like a dense checkout with a few exceptions (e.g. branch checkouts and switches write fewer things, knowing the VFS will lazily write the rest on an as-needed basis). -Since there is no publically available VFS-related code for folks to try, +Since there is no publicly available VFS-related code for folks to try, the number of folks who can test such a usecase is limited. The primary reason to note the Behavior C usecase is that as we fix things diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index ee71e9d8aa..38e920725c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.46.GIT +DEF_VER=v2.47.0-rc0 LF=' ' @@ -1356,6 +1356,7 @@ UNIT_TEST_PROGRAMS += t-reftable-basics UNIT_TEST_PROGRAMS += t-reftable-block UNIT_TEST_PROGRAMS += t-reftable-merged UNIT_TEST_PROGRAMS += t-reftable-pq +UNIT_TEST_PROGRAMS += t-reftable-reader UNIT_TEST_PROGRAMS += t-reftable-readwrite UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-reftable-stack @@ -1365,9 +1366,9 @@ UNIT_TEST_PROGRAMS += t-strcmp-offset UNIT_TEST_PROGRAMS += t-trailer UNIT_TEST_PROGRAMS += t-urlmatch-normalization UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) -UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o +UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o # xdiff and reftable libs may in turn depend on what is in libgit.a GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE) @@ -2725,6 +2726,7 @@ OBJECTS += $(FUZZ_OBJS) OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) OBJECTS += $(UNIT_TEST_OBJS) OBJECTS += $(CLAR_TEST_OBJS) +OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS)) ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o @@ -3864,9 +3866,7 @@ $(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS -Wl,--allow-multiple-definition \ $(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE) -$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \ - $(UNIT_TEST_DIR)/test-lib.o \ - $(UNIT_TEST_DIR)/lib-oid.o \ +$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_OBJS) \ $(GITLIBS) GIT-LDFLAGS $(call mkdir_p_parent_template) $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \ @@ -278,13 +278,26 @@ struct line { * This represents a "file", which is an array of "lines". */ struct image { - char *buf; - size_t len; - size_t nr; - size_t alloc; - struct line *line_allocated; + struct strbuf buf; struct line *line; + size_t line_nr, line_alloc; }; +#define IMAGE_INIT { \ + .buf = STRBUF_INIT, \ +} + +static void image_init(struct image *image) +{ + struct image empty = IMAGE_INIT; + memcpy(image, &empty, sizeof(*image)); +} + +static void image_clear(struct image *image) +{ + strbuf_release(&image->buf); + free(image->line); + image_init(image); +} static uint32_t hash_line(const char *cp, size_t len) { @@ -298,49 +311,13 @@ static uint32_t hash_line(const char *cp, size_t len) return h; } -/* - * Compare lines s1 of length n1 and s2 of length n2, ignoring - * whitespace difference. Returns 1 if they match, 0 otherwise - */ -static int fuzzy_matchlines(const char *s1, size_t n1, - const char *s2, size_t n2) +static void image_add_line(struct image *img, const char *bol, size_t len, unsigned flag) { - const char *end1 = s1 + n1; - const char *end2 = s2 + n2; - - /* ignore line endings */ - while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n')) - end1--; - while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n')) - end2--; - - while (s1 < end1 && s2 < end2) { - if (isspace(*s1)) { - /* - * Skip whitespace. We check on both buffers - * because we don't want "a b" to match "ab". - */ - if (!isspace(*s2)) - return 0; - while (s1 < end1 && isspace(*s1)) - s1++; - while (s2 < end2 && isspace(*s2)) - s2++; - } else if (*s1++ != *s2++) - return 0; - } - - /* If we reached the end on one side only, lines don't match. */ - return s1 == end1 && s2 == end2; -} - -static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag) -{ - ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc); - img->line_allocated[img->nr].len = len; - img->line_allocated[img->nr].hash = hash_line(bol, len); - img->line_allocated[img->nr].flag = flag; - img->nr++; + ALLOC_GROW(img->line, img->line_nr + 1, img->line_alloc); + img->line[img->line_nr].len = len; + img->line[img->line_nr].hash = hash_line(bol, len); + img->line[img->line_nr].flag = flag; + img->line_nr++; } /* @@ -348,37 +325,43 @@ static void add_line_info(struct image *img, const char *bol, size_t len, unsign * attach it to "image" and add line-based index to it. * "image" now owns the "buf". */ -static void prepare_image(struct image *image, char *buf, size_t len, +static void image_prepare(struct image *image, char *buf, size_t len, int prepare_linetable) { const char *cp, *ep; - memset(image, 0, sizeof(*image)); - image->buf = buf; - image->len = len; + image_clear(image); + strbuf_attach(&image->buf, buf, len, len + 1); if (!prepare_linetable) return; - ep = image->buf + image->len; - cp = image->buf; + ep = image->buf.buf + image->buf.len; + cp = image->buf.buf; while (cp < ep) { const char *next; for (next = cp; next < ep && *next != '\n'; next++) ; if (next < ep) next++; - add_line_info(image, cp, next - cp, 0); + image_add_line(image, cp, next - cp, 0); cp = next; } - image->line = image->line_allocated; } -static void clear_image(struct image *image) +static void image_remove_first_line(struct image *img) { - free(image->buf); - free(image->line_allocated); - memset(image, 0, sizeof(*image)); + strbuf_remove(&img->buf, 0, img->line[0].len); + img->line_nr--; + if (img->line_nr) + MOVE_ARRAY(img->line, img->line + 1, img->line_nr); +} + +static void image_remove_last_line(struct image *img) +{ + size_t last_line_len = img->line[img->line_nr - 1].len; + strbuf_setlen(&img->buf, img->buf.len - last_line_len); + img->line_nr--; } /* fmt must contain _one_ %s and no other substitution */ @@ -2327,65 +2310,43 @@ static int read_old_data(struct stat *st, struct patch *patch, /* * Update the preimage, and the common lines in postimage, - * from buffer buf of length len. If postlen is 0 the postimage - * is updated in place, otherwise it's updated on a new buffer - * of length postlen + * from buffer buf of length len. */ - static void update_pre_post_images(struct image *preimage, struct image *postimage, - char *buf, - size_t len, size_t postlen) + char *buf, size_t len) { + struct image fixed_preimage = IMAGE_INIT; + size_t insert_pos = 0; int i, ctx, reduced; - char *new_buf, *old_buf, *fixed; - struct image fixed_preimage; + const char *fixed; /* * Update the preimage with whitespace fixes. Note that we * are not losing preimage->buf -- apply_one_fragment() will * free "oldlines". */ - prepare_image(&fixed_preimage, buf, len, 1); - assert(postlen - ? fixed_preimage.nr == preimage->nr - : fixed_preimage.nr <= preimage->nr); - for (i = 0; i < fixed_preimage.nr; i++) + image_prepare(&fixed_preimage, buf, len, 1); + for (i = 0; i < fixed_preimage.line_nr; i++) fixed_preimage.line[i].flag = preimage->line[i].flag; - free(preimage->line_allocated); + image_clear(preimage); *preimage = fixed_preimage; + fixed = preimage->buf.buf; /* - * Adjust the common context lines in postimage. This can be - * done in-place when we are shrinking it with whitespace - * fixing, but needs a new buffer when ignoring whitespace or - * expanding leading tabs to spaces. - * - * We trust the caller to tell us if the update can be done - * in place (postlen==0) or not. + * Adjust the common context lines in postimage. */ - old_buf = postimage->buf; - if (postlen) - new_buf = postimage->buf = xmalloc(postlen); - else - new_buf = old_buf; - fixed = preimage->buf; - - for (i = reduced = ctx = 0; i < postimage->nr; i++) { + for (i = reduced = ctx = 0; i < postimage->line_nr; i++) { size_t l_len = postimage->line[i].len; + if (!(postimage->line[i].flag & LINE_COMMON)) { /* an added line -- no counterparts in preimage */ - memmove(new_buf, old_buf, l_len); - old_buf += l_len; - new_buf += l_len; + insert_pos += l_len; continue; } - /* a common context -- skip it in the original postimage */ - old_buf += l_len; - /* and find the corresponding one in the fixed preimage */ - while (ctx < preimage->nr && + while (ctx < preimage->line_nr && !(preimage->line[ctx].flag & LINE_COMMON)) { fixed += preimage->line[ctx].len; ctx++; @@ -2395,29 +2356,59 @@ static void update_pre_post_images(struct image *preimage, * preimage is expected to run out, if the caller * fixed addition of trailing blank lines. */ - if (preimage->nr <= ctx) { + if (preimage->line_nr <= ctx) { reduced++; continue; } /* and copy it in, while fixing the line length */ l_len = preimage->line[ctx].len; - memcpy(new_buf, fixed, l_len); - new_buf += l_len; + strbuf_splice(&postimage->buf, insert_pos, postimage->line[i].len, + fixed, l_len); + insert_pos += l_len; fixed += l_len; postimage->line[i].len = l_len; ctx++; } - if (postlen - ? postlen < new_buf - postimage->buf - : postimage->len < new_buf - postimage->buf) - BUG("caller miscounted postlen: asked %d, orig = %d, used = %d", - (int)postlen, (int) postimage->len, (int)(new_buf - postimage->buf)); - /* Fix the length of the whole thing */ - postimage->len = new_buf - postimage->buf; - postimage->nr -= reduced; + postimage->line_nr -= reduced; +} + +/* + * Compare lines s1 of length n1 and s2 of length n2, ignoring + * whitespace difference. Returns 1 if they match, 0 otherwise + */ +static int fuzzy_matchlines(const char *s1, size_t n1, + const char *s2, size_t n2) +{ + const char *end1 = s1 + n1; + const char *end2 = s2 + n2; + + /* ignore line endings */ + while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n')) + end1--; + while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n')) + end2--; + + while (s1 < end1 && s2 < end2) { + if (isspace(*s1)) { + /* + * Skip whitespace. We check on both buffers + * because we don't want "a b" to match "ab". + */ + if (!isspace(*s2)) + return 0; + while (s1 < end1 && isspace(*s1)) + s1++; + while (s2 < end2 && isspace(*s2)) + s2++; + } else if (*s1++ != *s2++) + return 0; + } + + /* If we reached the end on one side only, lines don't match. */ + return s1 == end1 && s2 == end2; } static int line_by_line_fuzzy_match(struct image *img, @@ -2430,7 +2421,6 @@ static int line_by_line_fuzzy_match(struct image *img, int i; size_t imgoff = 0; size_t preoff = 0; - size_t postlen = postimage->len; size_t extra_chars; char *buf; char *preimage_eof; @@ -2443,11 +2433,9 @@ static int line_by_line_fuzzy_match(struct image *img, size_t prelen = preimage->line[i].len; size_t imglen = img->line[current_lno+i].len; - if (!fuzzy_matchlines(img->buf + current + imgoff, imglen, - preimage->buf + preoff, prelen)) + if (!fuzzy_matchlines(img->buf.buf + current + imgoff, imglen, + preimage->buf.buf + preoff, prelen)) return 0; - if (preimage->line[i].flag & LINE_COMMON) - postlen += imglen - prelen; imgoff += imglen; preoff += prelen; } @@ -2463,10 +2451,10 @@ static int line_by_line_fuzzy_match(struct image *img, * are whitespace characters. (This can only happen if * we are removing blank lines at the end of the file.) */ - buf = preimage_eof = preimage->buf + preoff; - for ( ; i < preimage->nr; i++) + buf = preimage_eof = preimage->buf.buf + preoff; + for ( ; i < preimage->line_nr; i++) preoff += preimage->line[i].len; - preimage_end = preimage->buf + preoff; + preimage_end = preimage->buf.buf + preoff; for ( ; buf < preimage_end; buf++) if (!isspace(*buf)) return 0; @@ -2480,11 +2468,11 @@ static int line_by_line_fuzzy_match(struct image *img, */ extra_chars = preimage_end - preimage_eof; strbuf_init(&fixed, imgoff + extra_chars); - strbuf_add(&fixed, img->buf + current, imgoff); + strbuf_add(&fixed, img->buf.buf + current, imgoff); strbuf_add(&fixed, preimage_eof, extra_chars); fixed_buf = strbuf_detach(&fixed, &fixed_len); update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); + fixed_buf, fixed_len); return 1; } @@ -2500,16 +2488,17 @@ static int match_fragment(struct apply_state *state, int i; const char *orig, *target; struct strbuf fixed = STRBUF_INIT; - size_t postlen; + char *fixed_buf; + size_t fixed_len; int preimage_limit; int ret; - if (preimage->nr + current_lno <= img->nr) { + if (preimage->line_nr + current_lno <= img->line_nr) { /* * The hunk falls within the boundaries of img. */ - preimage_limit = preimage->nr; - if (match_end && (preimage->nr + current_lno != img->nr)) { + preimage_limit = preimage->line_nr; + if (match_end && (preimage->line_nr + current_lno != img->line_nr)) { ret = 0; goto out; } @@ -2522,7 +2511,7 @@ static int match_fragment(struct apply_state *state, * match with img, and the remainder of the preimage * must be blank. */ - preimage_limit = img->nr - current_lno; + preimage_limit = img->line_nr - current_lno; } else { /* * The hunk extends beyond the end of the img and @@ -2547,7 +2536,7 @@ static int match_fragment(struct apply_state *state, } } - if (preimage_limit == preimage->nr) { + if (preimage_limit == preimage->line_nr) { /* * Do we have an exact match? If we were told to match * at the end, size must be exactly at current+fragsize, @@ -2556,9 +2545,9 @@ static int match_fragment(struct apply_state *state, * exactly. */ if ((match_end - ? (current + preimage->len == img->len) - : (current + preimage->len <= img->len)) && - !memcmp(img->buf + current, preimage->buf, preimage->len)) { + ? (current + preimage->buf.len == img->buf.len) + : (current + preimage->buf.len <= img->buf.len)) && + !memcmp(img->buf.buf + current, preimage->buf.buf, preimage->buf.len)) { ret = 1; goto out; } @@ -2572,7 +2561,7 @@ static int match_fragment(struct apply_state *state, */ const char *buf, *buf_end; - buf = preimage->buf; + buf = preimage->buf.buf; buf_end = buf; for (i = 0; i < preimage_limit; i++) buf_end += preimage->line[i].len; @@ -2617,21 +2606,14 @@ static int match_fragment(struct apply_state *state, * fixed. */ - /* First count added lines in postimage */ - postlen = 0; - for (i = 0; i < postimage->nr; i++) { - if (!(postimage->line[i].flag & LINE_COMMON)) - postlen += postimage->line[i].len; - } - /* * The preimage may extend beyond the end of the file, * but in this loop we will only handle the part of the * preimage that falls within the file. */ - strbuf_grow(&fixed, preimage->len + 1); - orig = preimage->buf; - target = img->buf + current; + strbuf_grow(&fixed, preimage->buf.len + 1); + orig = preimage->buf.buf; + target = img->buf.buf + current; for (i = 0; i < preimage_limit; i++) { size_t oldlen = preimage->line[i].len; size_t tgtlen = img->line[current_lno + i].len; @@ -2660,10 +2642,6 @@ static int match_fragment(struct apply_state *state, !memcmp(tgtfix.buf, fixed.buf + fixstart, fixed.len - fixstart)); - /* Add the length if this is common with the postimage */ - if (preimage->line[i].flag & LINE_COMMON) - postlen += tgtfix.len; - strbuf_release(&tgtfix); if (!match) { ret = 0; @@ -2681,7 +2659,7 @@ static int match_fragment(struct apply_state *state, * empty or only contain whitespace (if WS_BLANK_AT_EOL is * false). */ - for ( ; i < preimage->nr; i++) { + for ( ; i < preimage->line_nr; i++) { size_t fixstart = fixed.len; /* start of the fixed preimage */ size_t oldlen = preimage->line[i].len; int j; @@ -2705,10 +2683,9 @@ static int match_fragment(struct apply_state *state, * has whitespace breakages unfixed, and fixing them makes the * hunk match. Update the context lines in the postimage. */ - if (postlen < postimage->len) - postlen = 0; + fixed_buf = strbuf_detach(&fixed, &fixed_len); update_pre_post_images(preimage, postimage, - fixed.buf, fixed.len, postlen); + fixed_buf, fixed_len); ret = 1; @@ -2736,7 +2713,7 @@ static int find_pos(struct apply_state *state, * than `match_beginning`. */ if (state->allow_overlap && match_beginning && match_end && - img->nr - preimage->nr != 0) + img->line_nr - preimage->line_nr != 0) match_beginning = 0; /* @@ -2747,15 +2724,15 @@ static int find_pos(struct apply_state *state, if (match_beginning) line = 0; else if (match_end) - line = img->nr - preimage->nr; + line = img->line_nr - preimage->line_nr; /* * Because the comparison is unsigned, the following test * will also take care of a negative line number that can * result when match_end and preimage is larger than the target. */ - if ((size_t) line > img->nr) - line = img->nr; + if ((size_t) line > img->line_nr) + line = img->line_nr; current = 0; for (i = 0; i < line; i++) @@ -2778,7 +2755,7 @@ static int find_pos(struct apply_state *state, return current_lno; again: - if (backwards_lno == 0 && forwards_lno == img->nr) + if (backwards_lno == 0 && forwards_lno == img->line_nr) break; if (i & 1) { @@ -2791,7 +2768,7 @@ static int find_pos(struct apply_state *state, current = backwards; current_lno = backwards_lno; } else { - if (forwards_lno == img->nr) { + if (forwards_lno == img->line_nr) { i++; goto again; } @@ -2805,19 +2782,6 @@ static int find_pos(struct apply_state *state, return -1; } -static void remove_first_line(struct image *img) -{ - img->buf += img->line[0].len; - img->len -= img->line[0].len; - img->line++; - img->nr--; -} - -static void remove_last_line(struct image *img) -{ - img->len -= img->line[--img->nr].len; -} - /* * The change from "preimage" and "postimage" has been found to * apply at applied_pos (counts in line numbers) in "img". @@ -2835,6 +2799,7 @@ static void update_image(struct apply_state *state, */ int i, nr; size_t remove_count, insert_count, applied_at = 0; + size_t result_alloc; char *result; int preimage_limit; @@ -2847,9 +2812,9 @@ static void update_image(struct apply_state *state, * to the number of lines in the preimage that falls * within the boundaries. */ - preimage_limit = preimage->nr; - if (preimage_limit > img->nr - applied_pos) - preimage_limit = img->nr - applied_pos; + preimage_limit = preimage->line_nr; + if (preimage_limit > img->line_nr - applied_pos) + preimage_limit = img->line_nr - applied_pos; for (i = 0; i < applied_pos; i++) applied_at += img->line[i].len; @@ -2857,39 +2822,36 @@ static void update_image(struct apply_state *state, remove_count = 0; for (i = 0; i < preimage_limit; i++) remove_count += img->line[applied_pos + i].len; - insert_count = postimage->len; + insert_count = postimage->buf.len; /* Adjust the contents */ - result = xmalloc(st_add3(st_sub(img->len, remove_count), insert_count, 1)); - memcpy(result, img->buf, applied_at); - memcpy(result + applied_at, postimage->buf, postimage->len); - memcpy(result + applied_at + postimage->len, - img->buf + (applied_at + remove_count), - img->len - (applied_at + remove_count)); - free(img->buf); - img->buf = result; - img->len += insert_count - remove_count; - result[img->len] = '\0'; + result_alloc = st_add3(st_sub(img->buf.len, remove_count), insert_count, 1); + result = xmalloc(result_alloc); + memcpy(result, img->buf.buf, applied_at); + memcpy(result + applied_at, postimage->buf.buf, postimage->buf.len); + memcpy(result + applied_at + postimage->buf.len, + img->buf.buf + (applied_at + remove_count), + img->buf.len - (applied_at + remove_count)); + strbuf_attach(&img->buf, result, postimage->buf.len + img->buf.len - remove_count, + result_alloc); /* Adjust the line table */ - nr = img->nr + postimage->nr - preimage_limit; - if (preimage_limit < postimage->nr) { + nr = img->line_nr + postimage->line_nr - preimage_limit; + if (preimage_limit < postimage->line_nr) /* - * NOTE: this knows that we never call remove_first_line() + * NOTE: this knows that we never call image_remove_first_line() * on anything other than pre/post image. */ REALLOC_ARRAY(img->line, nr); - img->line_allocated = img->line; - } - if (preimage_limit != postimage->nr) - MOVE_ARRAY(img->line + applied_pos + postimage->nr, + if (preimage_limit != postimage->line_nr) + MOVE_ARRAY(img->line + applied_pos + postimage->line_nr, img->line + applied_pos + preimage_limit, - img->nr - (applied_pos + preimage_limit)); - COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->nr); + img->line_nr - (applied_pos + preimage_limit)); + COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->line_nr); if (!state->allow_overlap) - for (i = 0; i < postimage->nr; i++) + for (i = 0; i < postimage->line_nr; i++) img->line[applied_pos + i].flag |= LINE_PATCHED; - img->nr = nr; + img->line_nr = nr; } /* @@ -2912,11 +2874,9 @@ static int apply_one_fragment(struct apply_state *state, int hunk_linenr = frag->linenr; unsigned long leading, trailing; int pos, applied_pos; - struct image preimage; - struct image postimage; + struct image preimage = IMAGE_INIT; + struct image postimage = IMAGE_INIT; - memset(&preimage, 0, sizeof(preimage)); - memset(&postimage, 0, sizeof(postimage)); oldlines = xmalloc(size); strbuf_init(&newlines, size); @@ -2958,8 +2918,8 @@ static int apply_one_fragment(struct apply_state *state, break; *old++ = '\n'; strbuf_addch(&newlines, '\n'); - add_line_info(&preimage, "\n", 1, LINE_COMMON); - add_line_info(&postimage, "\n", 1, LINE_COMMON); + image_add_line(&preimage, "\n", 1, LINE_COMMON); + image_add_line(&postimage, "\n", 1, LINE_COMMON); is_blank_context = 1; break; case ' ': @@ -2969,7 +2929,7 @@ static int apply_one_fragment(struct apply_state *state, /* fallthrough */ case '-': memcpy(old, patch + 1, plen); - add_line_info(&preimage, old, plen, + image_add_line(&preimage, old, plen, (first == ' ' ? LINE_COMMON : 0)); old += plen; if (first == '-') @@ -2989,7 +2949,7 @@ static int apply_one_fragment(struct apply_state *state, else { ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws); } - add_line_info(&postimage, newlines.buf + start, newlines.len - start, + image_add_line(&postimage, newlines.buf + start, newlines.len - start, (first == '+' ? 0 : LINE_COMMON)); if (first == '+' && (ws_rule & WS_BLANK_AT_EOF) && @@ -3023,8 +2983,8 @@ static int apply_one_fragment(struct apply_state *state, newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') { old--; strbuf_setlen(&newlines, newlines.len - 1); - preimage.line_allocated[preimage.nr - 1].len--; - postimage.line_allocated[postimage.nr - 1].len--; + preimage.line[preimage.line_nr - 1].len--; + postimage.line[postimage.line_nr - 1].len--; } leading = frag->leading; @@ -3054,12 +3014,8 @@ static int apply_one_fragment(struct apply_state *state, match_end = !state->unidiff_zero && !trailing; pos = frag->newpos ? (frag->newpos - 1) : 0; - preimage.buf = oldlines; - preimage.len = old - oldlines; - postimage.buf = newlines.buf; - postimage.len = newlines.len; - preimage.line = preimage.line_allocated; - postimage.line = postimage.line_allocated; + strbuf_add(&preimage.buf, oldlines, old - oldlines); + strbuf_swap(&postimage.buf, &newlines); for (;;) { @@ -3083,28 +3039,28 @@ static int apply_one_fragment(struct apply_state *state, * just reduce the larger context. */ if (leading >= trailing) { - remove_first_line(&preimage); - remove_first_line(&postimage); + image_remove_first_line(&preimage); + image_remove_first_line(&postimage); pos--; leading--; } if (trailing > leading) { - remove_last_line(&preimage); - remove_last_line(&postimage); + image_remove_last_line(&preimage); + image_remove_last_line(&postimage); trailing--; } } if (applied_pos >= 0) { if (new_blank_lines_at_end && - preimage.nr + applied_pos >= img->nr && + preimage.line_nr + applied_pos >= img->line_nr && (ws_rule & WS_BLANK_AT_EOF) && state->ws_error_action != nowarn_ws_error) { record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, found_new_blank_lines_at_end); if (state->ws_error_action == correct_ws_error) { while (new_blank_lines_at_end--) - remove_last_line(&postimage); + image_remove_last_line(&postimage); } /* * We would want to prevent write_out_results() @@ -3147,8 +3103,8 @@ static int apply_one_fragment(struct apply_state *state, out: free(oldlines); strbuf_release(&newlines); - free(preimage.line_allocated); - free(postimage.line_allocated); + image_clear(&preimage); + image_clear(&postimage); return (applied_pos < 0); } @@ -3178,18 +3134,16 @@ static int apply_binary_fragment(struct apply_state *state, } switch (fragment->binary_patch_method) { case BINARY_DELTA_DEFLATED: - dst = patch_delta(img->buf, img->len, fragment->patch, + dst = patch_delta(img->buf.buf, img->buf.len, fragment->patch, fragment->size, &len); if (!dst) return -1; - clear_image(img); - img->buf = dst; - img->len = len; + image_clear(img); + strbuf_attach(&img->buf, dst, len, len + 1); return 0; case BINARY_LITERAL_DEFLATED: - clear_image(img); - img->len = fragment->size; - img->buf = xmemdupz(fragment->patch, img->len); + image_clear(img); + strbuf_add(&img->buf, fragment->patch, fragment->size); return 0; } return -1; @@ -3225,8 +3179,8 @@ static int apply_binary(struct apply_state *state, * See if the old one matches what the patch * applies to. */ - hash_object_file(the_hash_algo, img->buf, img->len, OBJ_BLOB, - &oid); + hash_object_file(the_hash_algo, img->buf.buf, img->buf.len, + OBJ_BLOB, &oid); if (strcmp(oid_to_hex(&oid), patch->old_oid_prefix)) return error(_("the patch applies to '%s' (%s), " "which does not match the " @@ -3235,14 +3189,14 @@ static int apply_binary(struct apply_state *state, } else { /* Otherwise, the old one must be empty. */ - if (img->len) + if (img->buf.len) return error(_("the patch applies to an empty " "'%s' but it is not empty"), name); } get_oid_hex(patch->new_oid_prefix, &oid); if (is_null_oid(&oid)) { - clear_image(img); + image_clear(img); return 0; /* deletion patch */ } @@ -3258,9 +3212,8 @@ static int apply_binary(struct apply_state *state, return error(_("the necessary postimage %s for " "'%s' cannot be read"), patch->new_oid_prefix, name); - clear_image(img); - img->buf = result; - img->len = size; + image_clear(img); + strbuf_attach(&img->buf, result, size, size + 1); } else { /* * We have verified buf matches the preimage; @@ -3272,7 +3225,7 @@ static int apply_binary(struct apply_state *state, name); /* verify that the result matches */ - hash_object_file(the_hash_algo, img->buf, img->len, OBJ_BLOB, + hash_object_file(the_hash_algo, img->buf.buf, img->buf.len, OBJ_BLOB, &oid); if (strcmp(oid_to_hex(&oid), patch->new_oid_prefix)) return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"), @@ -3534,7 +3487,7 @@ static int load_preimage(struct apply_state *state, } img = strbuf_detach(&buf, &len); - prepare_image(image, img, len, !patch->is_binary); + image_prepare(image, img, len, !patch->is_binary); return 0; } @@ -3542,14 +3495,14 @@ static int resolve_to(struct image *image, const struct object_id *result_id) { unsigned long size; enum object_type type; + char *data; - clear_image(image); + image_clear(image); - image->buf = repo_read_object_file(the_repository, result_id, &type, - &size); - if (!image->buf || type != OBJ_BLOB) + data = repo_read_object_file(the_repository, result_id, &type, &size); + if (!data || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(result_id)); - image->len = size; + strbuf_attach(&image->buf, data, size, size + 1); return 0; } @@ -3592,9 +3545,8 @@ static int three_way_merge(struct apply_state *state, free(result.ptr); return -1; } - clear_image(image); - image->buf = result.ptr; - image->len = result.size; + image_clear(image); + strbuf_attach(&image->buf, result.ptr, result.size, result.size); return status; } @@ -3639,7 +3591,7 @@ static int load_current(struct apply_state *state, else if (status) return -1; img = strbuf_detach(&buf, &len); - prepare_image(image, img, len, !patch->is_binary); + image_prepare(image, img, len, !patch->is_binary); return 0; } @@ -3654,7 +3606,7 @@ static int try_threeway(struct apply_state *state, size_t len; int status; char *img; - struct image tmp_image; + struct image tmp_image = IMAGE_INIT; /* No point falling back to 3-way merge in these cases */ if (patch->is_delete || @@ -3674,15 +3626,15 @@ static int try_threeway(struct apply_state *state, fprintf(stderr, _("Performing three-way merge...\n")); img = strbuf_detach(&buf, &len); - prepare_image(&tmp_image, img, len, 1); + image_prepare(&tmp_image, img, len, 1); /* Apply the patch to get the post image */ if (apply_fragments(state, &tmp_image, patch) < 0) { - clear_image(&tmp_image); + image_clear(&tmp_image); return -1; } /* post_oid is theirs */ - write_object_file(tmp_image.buf, tmp_image.len, OBJ_BLOB, &post_oid); - clear_image(&tmp_image); + write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &post_oid); + image_clear(&tmp_image); /* our_oid is ours */ if (patch->is_new) { @@ -3694,8 +3646,8 @@ static int try_threeway(struct apply_state *state, return error(_("cannot read the current contents of '%s'"), patch->old_name); } - write_object_file(tmp_image.buf, tmp_image.len, OBJ_BLOB, &our_oid); - clear_image(&tmp_image); + write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &our_oid); + image_clear(&tmp_image); /* in-core three-way merge between post and our using pre as base */ status = three_way_merge(state, image, patch->new_name, @@ -3731,7 +3683,7 @@ static int try_threeway(struct apply_state *state, static int apply_data(struct apply_state *state, struct patch *patch, struct stat *st, const struct cache_entry *ce) { - struct image image; + struct image image = IMAGE_INIT; if (load_preimage(state, &image, patch, st, ce) < 0) return -1; @@ -3743,14 +3695,13 @@ static int apply_data(struct apply_state *state, struct patch *patch, /* Note: with --reject, apply_fragments() returns 0 */ if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) { - clear_image(&image); + image_clear(&image); return -1; } } - patch->result = image.buf; - patch->resultsize = image.len; + patch->result = strbuf_detach(&image.buf, &patch->resultsize); add_to_fn_table(state, patch); - free(image.line_allocated); + free(image.line); if (0 < patch->is_delete && patch->resultsize) return error(_("removal patch leaves file contents")); @@ -304,8 +304,6 @@ int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry) { struct archiver_context context; - struct unpack_trees_options opts; - struct tree_desc t; int err; struct strbuf path_in_archive = STRBUF_INIT; struct strbuf content = STRBUF_INIT; @@ -331,23 +329,6 @@ int write_archive_entries(struct archiver_args *args, context.args = args; context.write_entry = write_entry; - /* - * Setup index and instruct attr to read index only - */ - if (!args->worktree_attributes) { - memset(&opts, 0, sizeof(opts)); - opts.index_only = 1; - opts.head_idx = -1; - opts.src_index = args->repo->index; - opts.dst_index = args->repo->index; - opts.fn = oneway_merge; - init_tree_desc(&t, &args->tree->object.oid, - args->tree->buffer, args->tree->size); - if (unpack_trees(1, &t, &opts)) - return -1; - git_attr_set_direction(GIT_ATTR_INDEX); - } - err = read_tree(args->repo, args->tree, &args->pathspec, queue_or_write_archive_entry, @@ -540,6 +521,26 @@ static void parse_treeish_arg(const char **argv, if (!tree) die(_("not a tree object: %s"), oid_to_hex(&oid)); + /* + * Setup index and instruct attr to read index only + */ + if (!ar_args->worktree_attributes) { + struct unpack_trees_options opts; + struct tree_desc t; + + memset(&opts, 0, sizeof(opts)); + opts.index_only = 1; + opts.head_idx = -1; + opts.src_index = ar_args->repo->index; + opts.dst_index = ar_args->repo->index; + opts.fn = oneway_merge; + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); + if (unpack_trees(1, &t, &opts)) + die(_("unable to checkout working tree")); + + git_attr_set_direction(GIT_ATTR_INDEX); + } + ar_args->refname = ref; ar_args->tree = tree; ar_args->commit_oid = commit_oid; diff --git a/builtin/difftool.c b/builtin/difftool.c index 4b416743ff..5772e82106 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -662,6 +662,12 @@ finish: if (fp) fclose(fp); + hashmap_clear_and_free(&working_tree_dups, struct working_tree_entry, entry); + hashmap_clear_and_free(&wt_modified, struct path_entry, entry); + hashmap_clear_and_free(&tmp_modified, struct path_entry, entry); + hashmap_clear_and_free(&submodules, struct pair_entry, entry); + hashmap_clear_and_free(&symlinks2, struct pair_entry, entry); + release_index(&wtindex); free(lbase_dir); free(rbase_dir); strbuf_release(&info); diff --git a/builtin/gc.c b/builtin/gc.c index c3894fb6bd..6a7a2da006 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1478,9 +1478,9 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts, static void initialize_maintenance_strategy(void) { - char *config_str; + const char *config_str; - if (git_config_get_string("maintenance.strategy", &config_str)) + if (git_config_get_string_tmp("maintenance.strategy", &config_str)) return; if (!strcasecmp(config_str, "incremental")) { @@ -1768,6 +1768,42 @@ static const char *get_frequency(enum schedule_priority schedule) } } +static const char *extraconfig[] = { + "credential.interactive=false", + "core.askPass=true", /* 'true' returns success, but no output. */ + NULL +}; + +static const char *get_extra_config_parameters(void) { + static const char *result = NULL; + struct strbuf builder = STRBUF_INIT; + + if (result) + return result; + + for (const char **s = extraconfig; s && *s; s++) + strbuf_addf(&builder, "-c %s ", *s); + + result = strbuf_detach(&builder, NULL); + return result; +} + +static const char *get_extra_launchctl_strings(void) { + static const char *result = NULL; + struct strbuf builder = STRBUF_INIT; + + if (result) + return result; + + for (const char **s = extraconfig; s && *s; s++) { + strbuf_addstr(&builder, "<string>-c</string>\n"); + strbuf_addf(&builder, "<string>%s</string>\n", *s); + } + + result = strbuf_detach(&builder, NULL); + return result; +} + /* * get_schedule_cmd` reads the GIT_TEST_MAINT_SCHEDULER environment variable * to mock the schedulers that `git maintenance start` rely on. @@ -1782,32 +1818,33 @@ static const char *get_frequency(enum schedule_priority schedule) * * If $GIT_TEST_MAINT_SCHEDULER is set, return true. * In this case, the *cmd value is read as input. * - * * if the input value *cmd is the key of one of the comma-separated list - * item, then *is_available is set to true and *cmd is modified and becomes + * * if the input value cmd is the key of one of the comma-separated list + * item, then *is_available is set to true and *out is set to * the mock command. * * * if the input value *cmd isn’t the key of any of the comma-separated list - * item, then *is_available is set to false. + * item, then *is_available is set to false and *out is set to the original + * command. * * Ex.: * GIT_TEST_MAINT_SCHEDULER not set * +-------+-------------------------------------------------+ * | Input | Output | - * | *cmd | return code | *cmd | *is_available | + * | *cmd | return code | *out | *is_available | * +-------+-------------+-------------------+---------------+ - * | "foo" | false | "foo" (unchanged) | (unchanged) | + * | "foo" | false | NULL | (unchanged) | * +-------+-------------+-------------------+---------------+ * * GIT_TEST_MAINT_SCHEDULER set to “foo:./mock_foo.sh,bar:./mock_bar.sh” * +-------+-------------------------------------------------+ * | Input | Output | - * | *cmd | return code | *cmd | *is_available | + * | *cmd | return code | *out | *is_available | * +-------+-------------+-------------------+---------------+ * | "foo" | true | "./mock.foo.sh" | true | - * | "qux" | true | "qux" (unchanged) | false | + * | "qux" | true | "qux" (allocated) | false | * +-------+-------------+-------------------+---------------+ */ -static int get_schedule_cmd(const char **cmd, int *is_available) +static int get_schedule_cmd(const char *cmd, int *is_available, char **out) { char *testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER")); struct string_list_item *item; @@ -1826,16 +1863,22 @@ static int get_schedule_cmd(const char **cmd, int *is_available) if (string_list_split_in_place(&pair, item->string, ":", 2) != 2) continue; - if (!strcmp(*cmd, pair.items[0].string)) { - *cmd = pair.items[1].string; + if (!strcmp(cmd, pair.items[0].string)) { + if (out) + *out = xstrdup(pair.items[1].string); if (is_available) *is_available = 1; - string_list_clear(&list, 0); - UNLEAK(testing); - return 1; + string_list_clear(&pair, 0); + goto out; } + + string_list_clear(&pair, 0); } + if (out) + *out = xstrdup(cmd); + +out: string_list_clear(&list, 0); free(testing); return 1; @@ -1852,9 +1895,8 @@ static int get_random_minute(void) static int is_launchctl_available(void) { - const char *cmd = "launchctl"; int is_available; - if (get_schedule_cmd(&cmd, &is_available)) + if (get_schedule_cmd("launchctl", &is_available, NULL)) return is_available; #ifdef __APPLE__ @@ -1892,12 +1934,12 @@ static char *launchctl_get_uid(void) static int launchctl_boot_plist(int enable, const char *filename) { - const char *cmd = "launchctl"; + char *cmd; int result; struct child_process child = CHILD_PROCESS_INIT; char *uid = launchctl_get_uid(); - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("launchctl", NULL, &cmd); strvec_split(&child.args, cmd); strvec_pushl(&child.args, enable ? "bootstrap" : "bootout", uid, filename, NULL); @@ -1910,6 +1952,7 @@ static int launchctl_boot_plist(int enable, const char *filename) result = finish_command(&child); + free(cmd); free(uid); return result; } @@ -1961,10 +2004,10 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit static unsigned long lock_file_timeout_ms = ULONG_MAX; struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT; struct stat st; - const char *cmd = "launchctl"; + char *cmd; int minute = get_random_minute(); - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("launchctl", NULL, &cmd); preamble = "<?xml version=\"1.0\"?>\n" "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "<plist version=\"1.0\">" @@ -1974,6 +2017,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit "<array>\n" "<string>%s/git</string>\n" "<string>--exec-path=%s</string>\n" + "%s" /* For extra config parameters. */ "<string>for-each-repo</string>\n" "<string>--keep-going</string>\n" "<string>--config=maintenance.repo</string>\n" @@ -1983,7 +2027,8 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit "</array>\n" "<key>StartCalendarInterval</key>\n" "<array>\n"; - strbuf_addf(&plist, preamble, name, exec_path, exec_path, frequency); + strbuf_addf(&plist, preamble, name, exec_path, exec_path, + get_extra_launchctl_strings(), frequency); switch (schedule) { case SCHEDULE_HOURLY: @@ -2054,6 +2099,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit free(filename); free(name); + free(cmd); strbuf_release(&plist); strbuf_release(&plist2); return 0; @@ -2078,9 +2124,8 @@ static int launchctl_update_schedule(int run_maintenance, int fd UNUSED) static int is_schtasks_available(void) { - const char *cmd = "schtasks"; int is_available; - if (get_schedule_cmd(&cmd, &is_available)) + if (get_schedule_cmd("schtasks", &is_available, NULL)) return is_available; #ifdef GIT_WINDOWS_NATIVE @@ -2099,15 +2144,16 @@ static char *schtasks_task_name(const char *frequency) static int schtasks_remove_task(enum schedule_priority schedule) { - const char *cmd = "schtasks"; + char *cmd; struct child_process child = CHILD_PROCESS_INIT; const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("schtasks", NULL, &cmd); strvec_split(&child.args, cmd); strvec_pushl(&child.args, "/delete", "/tn", name, "/f", NULL); free(name); + free(cmd); return run_command(&child); } @@ -2121,7 +2167,7 @@ static int schtasks_remove_tasks(void) static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule) { - const char *cmd = "schtasks"; + char *cmd; int result; struct child_process child = CHILD_PROCESS_INIT; const char *xml; @@ -2131,7 +2177,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority struct strbuf tfilename = STRBUF_INIT; int minute = get_random_minute(); - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("schtasks", NULL, &cmd); strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX", repo_get_common_dir(the_repository), frequency); @@ -2218,11 +2264,12 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Actions Context=\"Author\">\n" "<Exec>\n" "<Command>\"%s\\headless-git.exe\"</Command>\n" - "<Arguments>--exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n" + "<Arguments>--exec-path=\"%s\" %s for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%s</Arguments>\n" "</Exec>\n" "</Actions>\n" "</Task>\n"; - fprintf(tfile->fp, xml, exec_path, exec_path, frequency); + fprintf(tfile->fp, xml, exec_path, exec_path, + get_extra_config_parameters(), frequency); strvec_split(&child.args, cmd); strvec_pushl(&child.args, "/create", "/tn", name, "/f", "/xml", get_tempfile_path(tfile), NULL); @@ -2237,6 +2284,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority delete_tempfile(&tfile); free(name); + free(cmd); return result; } @@ -2278,21 +2326,28 @@ static int check_crontab_process(const char *cmd) static int is_crontab_available(void) { - const char *cmd = "crontab"; + char *cmd; int is_available; + int ret; - if (get_schedule_cmd(&cmd, &is_available)) - return is_available; + if (get_schedule_cmd("crontab", &is_available, &cmd)) { + ret = is_available; + goto out; + } #ifdef __APPLE__ /* * macOS has cron, but it requires special permissions and will * create a UI alert when attempting to run this command. */ - return 0; + ret = 0; #else - return check_crontab_process(cmd); + ret = check_crontab_process(cmd); #endif + +out: + free(cmd); + return ret; } #define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE" @@ -2300,7 +2355,7 @@ static int is_crontab_available(void) static int crontab_update_schedule(int run_maintenance, int fd) { - const char *cmd = "crontab"; + char *cmd; int result = 0; int in_old_region = 0; struct child_process crontab_list = CHILD_PROCESS_INIT; @@ -2310,15 +2365,17 @@ static int crontab_update_schedule(int run_maintenance, int fd) struct tempfile *tmpedit = NULL; int minute = get_random_minute(); - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("crontab", NULL, &cmd); strvec_split(&crontab_list.args, cmd); strvec_push(&crontab_list.args, "-l"); crontab_list.in = -1; crontab_list.out = dup(fd); crontab_list.git_cmd = 0; - if (start_command(&crontab_list)) - return error(_("failed to run 'crontab -l'; your system might not support 'cron'")); + if (start_command(&crontab_list)) { + result = error(_("failed to run 'crontab -l'; your system might not support 'cron'")); + goto out; + } /* Ignore exit code, as an empty crontab will return error. */ finish_command(&crontab_list); @@ -2363,8 +2420,8 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n", - exec_path, exec_path); + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" %s for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%s\n", + exec_path, exec_path, get_extra_config_parameters()); fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly"); @@ -2388,8 +2445,10 @@ static int crontab_update_schedule(int run_maintenance, int fd) result = error(_("'crontab' died")); else fclose(cron_list); + out: delete_tempfile(&tmpedit); + free(cmd); return result; } @@ -2412,10 +2471,9 @@ static int real_is_systemd_timer_available(void) static int is_systemd_timer_available(void) { - const char *cmd = "systemctl"; int is_available; - if (get_schedule_cmd(&cmd, &is_available)) + if (get_schedule_cmd("systemctl", &is_available, NULL)) return is_available; return real_is_systemd_timer_available(); @@ -2564,7 +2622,7 @@ static int systemd_timer_write_service_template(const char *exec_path) "\n" "[Service]\n" "Type=oneshot\n" - "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n" + "ExecStart=\"%s/git\" --exec-path=\"%s\" %s for-each-repo --keep-going --config=maintenance.repo maintenance run --schedule=%%i\n" "LockPersonality=yes\n" "MemoryDenyWriteExecute=yes\n" "NoNewPrivileges=yes\n" @@ -2574,7 +2632,7 @@ static int systemd_timer_write_service_template(const char *exec_path) "RestrictSUIDSGID=yes\n" "SystemCallArchitectures=native\n" "SystemCallFilter=@system-service\n"; - if (fprintf(file, unit, exec_path, exec_path) < 0) { + if (fprintf(file, unit, exec_path, exec_path, get_extra_config_parameters()) < 0) { error(_("failed to write to '%s'"), filename); fclose(file); goto error; @@ -2596,9 +2654,10 @@ static int systemd_timer_enable_unit(int enable, enum schedule_priority schedule, int minute) { - const char *cmd = "systemctl"; + char *cmd = NULL; struct child_process child = CHILD_PROCESS_INIT; const char *frequency = get_frequency(schedule); + int ret; /* * Disabling the systemd unit while it is already disabled makes @@ -2609,20 +2668,25 @@ static int systemd_timer_enable_unit(int enable, * On the other hand, enabling a systemd unit which is already enabled * produces no error. */ - if (!enable) + if (!enable) { child.no_stderr = 1; - else if (systemd_timer_write_timer_file(schedule, minute)) - return -1; + } else if (systemd_timer_write_timer_file(schedule, minute)) { + ret = -1; + goto out; + } - get_schedule_cmd(&cmd, NULL); + get_schedule_cmd("systemctl", NULL, &cmd); strvec_split(&child.args, cmd); strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", "--now", NULL); strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer"); - if (start_command(&child)) - return error(_("failed to start systemctl")); - if (finish_command(&child)) + if (start_command(&child)) { + ret = error(_("failed to start systemctl")); + goto out; + } + + if (finish_command(&child)) { /* * Disabling an already disabled systemd unit makes * systemctl fail. @@ -2630,9 +2694,17 @@ static int systemd_timer_enable_unit(int enable, * * Enabling an enabled systemd unit doesn't fail. */ - if (enable) - return error(_("failed to run systemctl")); - return 0; + if (enable) { + ret = error(_("failed to run systemctl")); + goto out; + } + } + + ret = 0; + +out: + free(cmd); + return ret; } /* diff --git a/builtin/help.c b/builtin/help.c index 9c4cfe11d9..4a5a079070 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -54,7 +54,7 @@ static enum help_action { HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, } cmd_mode; -static const char *html_path; +static char *html_path; static int verbose = 1; static enum help_format help_format = HELP_FORMAT_NONE; static int exclude_guides; @@ -411,6 +411,7 @@ static int git_help_config(const char *var, const char *value, if (!strcmp(var, "help.htmlpath")) { if (!value) return config_error_nonbool(var); + free(html_path); html_path = xstrdup(value); return 0; } @@ -515,23 +516,24 @@ static void show_info_page(const char *page) static void get_html_page_path(struct strbuf *page_path, const char *page) { struct stat st; + const char *path = html_path; char *to_free = NULL; - if (!html_path) - html_path = to_free = system_path(GIT_HTML_PATH); + if (!path) + path = to_free = system_path(GIT_HTML_PATH); /* * Check that the page we're looking for exists. */ - if (!strstr(html_path, "://")) { - if (stat(mkpath("%s/%s.html", html_path, page), &st) + if (!strstr(path, "://")) { + if (stat(mkpath("%s/%s.html", path, page), &st) || !S_ISREG(st.st_mode)) die("'%s/%s.html': documentation file not found.", - html_path, page); + path, page); } strbuf_init(page_path, 0); - strbuf_addf(page_path, "%s/%s.html", html_path, page); + strbuf_addf(page_path, "%s/%s.html", path, page); free(to_free); } @@ -542,7 +544,7 @@ static void open_html(const char *path) static void show_html_page(const char *page) { - struct strbuf page_path; /* it leaks but we exec bellow */ + struct strbuf page_path; /* it leaks but we exec below */ get_html_page_path(&page_path, page); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 6aaba08e30..e016b0415d 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -507,7 +507,7 @@ static int get_common_prefix_len(const char *common_prefix) common_prefix_len = strlen(common_prefix); /* - * If the prefix has a trailing slash, strip it so that submodules wont + * If the prefix has a trailing slash, strip it so that submodules won't * be pruned from the index. */ if (common_prefix[common_prefix_len - 1] == '/') diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 7f08c38629..765eb20a93 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -65,7 +65,7 @@ static void set_commit_cutoff(struct commit *commit) static void adjust_cutoff_timestamp_for_slop(void) { if (cutoff) { - /* check for undeflow */ + /* check for underflow */ if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP) cutoff = cutoff - CUTOFF_DATE_SLOP; else diff --git a/builtin/pull.c b/builtin/pull.c index 149f201320..388ef3d130 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -85,7 +85,7 @@ static const char *opt_squash; static const char *opt_commit; static const char *opt_edit; static const char *cleanup_arg; -static const char *opt_ff; +static char *opt_ff; static const char *opt_verify_signatures; static const char *opt_verify; static int opt_autostash = -1; @@ -1028,8 +1028,10 @@ int cmd_pull(int argc, * "--rebase" can override a config setting of * pull.ff=only. */ - if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only")) - opt_ff = "--ff"; + if (opt_rebase >= 0 && opt_ff && !strcmp(opt_ff, "--ff-only")) { + free(opt_ff); + opt_ff = xstrdup("--ff"); + } } if (opt_rebase < 0) @@ -1139,7 +1141,8 @@ int cmd_pull(int argc, if (can_ff) { /* we can fast-forward this without invoking rebase */ - opt_ff = "--ff-only"; + free(opt_ff); + opt_ff = xstrdup("--ff-only"); ret = run_merge(); } else { ret = run_rebase(&newbase, &upstream); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c4b766c613..536d22761d 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -340,12 +340,26 @@ static void show_one_alternate_ref(const struct object_id *oid, static void write_head_info(void) { static struct oidset seen = OIDSET_INIT; + struct strvec excludes_vector = STRVEC_INIT; + const char **exclude_patterns; + + /* + * We need access to the reference names both with and without their + * namespace and thus cannot use `refs_for_each_namespaced_ref()`. We + * thus have to adapt exclude patterns to carry the namespace prefix + * ourselves. + */ + exclude_patterns = get_namespaced_exclude_patterns( + hidden_refs_to_excludes(&hidden_refs), + get_git_namespace(), &excludes_vector); refs_for_each_fullref_in(get_main_ref_store(the_repository), "", - hidden_refs_to_excludes(&hidden_refs), - show_ref_cb, &seen); + exclude_patterns, show_ref_cb, &seen); for_each_alternate_ref(show_one_alternate_ref, &seen); + oidset_clear(&seen); + strvec_clear(&excludes_vector); + if (!sent_capabilities) show_ref("capabilities^{}", null_oid()); @@ -1325,7 +1339,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si) } /* - * NEEDSWORK: we should consolidate various implementions of "are we + * NEEDSWORK: we should consolidate various implementations of "are we * on an unborn branch?" test into one, and make the unified one more * robust. !get_sha1() based check used here and elsewhere would not * allow us to tell an unborn branch from corrupt ref, for example. diff --git a/builtin/repack.c b/builtin/repack.c index cb4420f085..d6bb37e84a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -86,17 +86,34 @@ static int repack_config(const char *var, const char *value, run_update_server_info = git_config_bool(var, value); return 0; } - if (!strcmp(var, "repack.cruftwindow")) + if (!strcmp(var, "repack.cruftwindow")) { + free(cruft_po_args->window); return git_config_string(&cruft_po_args->window, var, value); - if (!strcmp(var, "repack.cruftwindowmemory")) + } + if (!strcmp(var, "repack.cruftwindowmemory")) { + free(cruft_po_args->window_memory); return git_config_string(&cruft_po_args->window_memory, var, value); - if (!strcmp(var, "repack.cruftdepth")) + } + if (!strcmp(var, "repack.cruftdepth")) { + free(cruft_po_args->depth); return git_config_string(&cruft_po_args->depth, var, value); - if (!strcmp(var, "repack.cruftthreads")) + } + if (!strcmp(var, "repack.cruftthreads")) { + free(cruft_po_args->threads); return git_config_string(&cruft_po_args->threads, var, value); + } return git_default_config(var, value, ctx, cb); } +static void pack_objects_args_release(struct pack_objects_args *args) +{ + free(args->window); + free(args->window_memory); + free(args->depth); + free(args->threads); + list_objects_filter_release(&args->filter_options); +} + struct existing_packs { struct string_list kept_packs; struct string_list non_kept_packs; @@ -1156,12 +1173,16 @@ int cmd_repack(int argc, const char *unpack_unreachable = NULL; int keep_unreachable = 0; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - struct pack_objects_args po_args = {NULL}; - struct pack_objects_args cruft_po_args = {NULL}; + struct pack_objects_args po_args = { 0 }; + struct pack_objects_args cruft_po_args = { 0 }; int write_midx = 0; const char *cruft_expiration = NULL; const char *expire_to = NULL; const char *filter_to = NULL; + const char *opt_window = NULL; + const char *opt_window_memory = NULL; + const char *opt_depth = NULL; + const char *opt_threads = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -1195,13 +1216,13 @@ int cmd_repack(int argc, N_("with -A, do not loosen objects older than this")), OPT_BOOL('k', "keep-unreachable", &keep_unreachable, N_("with -a, repack unreachable objects")), - OPT_STRING(0, "window", &po_args.window, N_("n"), + OPT_STRING(0, "window", &opt_window, N_("n"), N_("size of the window used for delta compression")), - OPT_STRING(0, "window-memory", &po_args.window_memory, N_("bytes"), + OPT_STRING(0, "window-memory", &opt_window_memory, N_("bytes"), N_("same as the above, but limit memory size instead of entries count")), - OPT_STRING(0, "depth", &po_args.depth, N_("n"), + OPT_STRING(0, "depth", &opt_depth, N_("n"), N_("limits the maximum delta depth")), - OPT_STRING(0, "threads", &po_args.threads, N_("n"), + OPT_STRING(0, "threads", &opt_threads, N_("n"), N_("limits the maximum number of threads")), OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size, N_("maximum size of each packfile")), @@ -1228,6 +1249,11 @@ int cmd_repack(int argc, argc = parse_options(argc, argv, prefix, builtin_repack_options, git_repack_usage, 0); + po_args.window = xstrdup_or_null(opt_window); + po_args.window_memory = xstrdup_or_null(opt_window_memory); + po_args.depth = xstrdup_or_null(opt_depth); + po_args.threads = xstrdup_or_null(opt_threads); + if (delete_redundant && repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); @@ -1393,13 +1419,13 @@ int cmd_repack(int argc, const char *pack_prefix = find_pack_prefix(packdir, packtmp); if (!cruft_po_args.window) - cruft_po_args.window = po_args.window; + cruft_po_args.window = xstrdup_or_null(po_args.window); if (!cruft_po_args.window_memory) - cruft_po_args.window_memory = po_args.window_memory; + cruft_po_args.window_memory = xstrdup_or_null(po_args.window_memory); if (!cruft_po_args.depth) - cruft_po_args.depth = po_args.depth; + cruft_po_args.depth = xstrdup_or_null(po_args.depth); if (!cruft_po_args.threads) - cruft_po_args.threads = po_args.threads; + cruft_po_args.threads = xstrdup_or_null(po_args.threads); if (!cruft_po_args.max_pack_size) cruft_po_args.max_pack_size = po_args.max_pack_size; @@ -1552,7 +1578,8 @@ cleanup: string_list_clear(&names, 1); existing_packs_release(&existing); free_pack_geometry(&geometry); - list_objects_filter_release(&po_args.filter_options); + pack_objects_args_release(&po_args); + pack_objects_args_release(&cruft_po_args); return ret; } diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index e4e791a4c9..49aedc1de8 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -924,6 +924,11 @@ static int sparse_checkout_disable(int argc, const char **argv, builtin_sparse_checkout_disable_options, builtin_sparse_checkout_disable_usage, 0); + /* + * Disable the advice message for expanding a sparse index, as we + * are expecting to do that when disabling sparse-checkout. + */ + give_advice_on_expansion = 0; repo_read_index(the_repository); memset(&pl, 0, sizeof(pl)); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a1ada86952..dc89488a7d 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -364,9 +364,13 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, if (!info->quiet) printf(_("Entering '%s'\n"), displaypath); - if (info->argv[0] && run_command(&cp)) - die(_("run_command returned non-zero status for %s\n."), - displaypath); + if (info->argv[0]) { + if (run_command(&cp)) + die(_("run_command returned non-zero status for %s\n."), + displaypath); + } else { + child_process_clear(&cp); + } if (info->recursive) { struct child_process cpr = CHILD_PROCESS_INIT; @@ -696,6 +700,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, if (flags & OPT_RECURSIVE) { struct child_process cpr = CHILD_PROCESS_INIT; + int res; cpr.git_cmd = 1; cpr.dir = path; @@ -711,7 +716,10 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, if (flags & OPT_QUIET) strvec_push(&cpr.args, "--quiet"); - if (run_command(&cpr)) + res = run_command(&cpr); + if (res == SIGPIPE + 128) + raise(SIGPIPE); + else if (res) die(_("failed to recurse into submodule '%s'"), path); } @@ -1618,6 +1626,8 @@ static int add_possible_reference_from_superproject( ; /* nothing */ } } + + strbuf_release(&err); strbuf_release(&sb); } @@ -2022,6 +2032,7 @@ struct update_data { static void update_data_release(struct update_data *ud) { free(ud->displaypath); + submodule_update_strategy_release(&ud->update_strategy); module_list_release(&ud->list); } @@ -2642,15 +2653,20 @@ static int update_submodule(struct update_data *update_data) if (!update_data->nofetch) { if (fetch_in_submodule(update_data->sm_path, update_data->depth, - 0, NULL)) + 0, NULL)) { + free(remote_ref); return die_message(_("Unable to fetch in submodule path '%s'"), update_data->sm_path); + } } if (repo_resolve_gitlink_ref(the_repository, update_data->sm_path, - remote_ref, &update_data->oid)) - return die_message(_("Unable to find %s revision in submodule path '%s'"), - remote_ref, update_data->sm_path); + remote_ref, &update_data->oid)) { + ret = die_message(_("Unable to find %s revision in submodule path '%s'"), + remote_ref, update_data->sm_path); + free(remote_ref); + return ret; + } free(remote_ref); } diff --git a/combine-diff.c b/combine-diff.c index 829a44e416..f6b624dc28 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1393,9 +1393,8 @@ static struct combine_diff_path *find_paths_generic(const struct object_id *oid, { struct combine_diff_path *paths = NULL; int i, num_parent = parents->nr; - int output_format = opt->output_format; - const char *orderfile = opt->orderfile; + char *orderfile = opt->orderfile; opt->output_format = DIFF_FORMAT_NO_OUTPUT; /* tell diff_tree to emit paths in sorted (=tree) order */ diff --git a/commit-graph.c b/commit-graph.c index aa6e08eb39..5bd89c0acd 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -2055,7 +2055,6 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) } if (safe_create_leading_directories(ctx->graph_name)) { - UNLEAK(ctx->graph_name); error(_("unable to create leading directories of %s"), ctx->graph_name); return -1; @@ -306,13 +306,16 @@ static int include_by_branch(struct config_include_data *data, int flags; int ret; struct strbuf pattern = STRBUF_INIT; - const char *refname = (!data->repo || !data->repo->gitdir) ? - NULL : refs_resolve_ref_unsafe(get_main_ref_store(data->repo), - "HEAD", 0, NULL, &flags); - const char *shortname; + const char *refname, *shortname; - if (!refname || !(flags & REF_ISSYMREF) || - !skip_prefix(refname, "refs/heads/", &shortname)) + if (!data->repo || data->repo->ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN) + return 0; + + refname = refs_resolve_ref_unsafe(get_main_ref_store(data->repo), + "HEAD", 0, NULL, &flags); + if (!refname || + !(flags & REF_ISSYMREF) || + !skip_prefix(refname, "refs/heads/", &shortname)) return 0; strbuf_add(&pattern, cond, cond_len); diff --git a/config.mak.dev b/config.mak.dev index 50026d1e0e..8eca7fa228 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -69,7 +69,7 @@ DEVELOPER_CFLAGS += -Wno-missing-braces endif endif -# Old versions of clang complain about initializaing a +# Old versions of clang complain about initializing a # struct-within-a-struct using just "{0}" rather than "{{0}}". This # error is considered a false-positive and not worth fixing, because # new clang versions do not, so just disable it. diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 608fd3fe70..62af7b33d2 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -970,18 +970,15 @@ if(BUILD_TESTING) add_executable(test-fake-ssh ${CMAKE_SOURCE_DIR}/t/helper/test-fake-ssh.c) target_link_libraries(test-fake-ssh common-main) -#reftable-tests -parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS") -list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/") - #unit-tests -add_library(unit-test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c) -add_library(unit-test-lib-oid OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/lib-oid.c) +parse_makefile_for_sources(unit-test_SOURCES "UNIT_TEST_OBJS") +list(TRANSFORM unit-test_SOURCES REPLACE "\\$\\(UNIT_TEST_DIR\\)/" "${CMAKE_SOURCE_DIR}/t/unit-tests/") +add_library(unit-test-lib STATIC ${unit-test_SOURCES}) parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "") foreach(unit_test ${unit_test_PROGRAMS}) add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c") - target_link_libraries("${unit_test}" unit-test-lib unit-test-lib-oid common-main) + target_link_libraries("${unit_test}" unit-test-lib common-main) set_target_properties("${unit_test}" PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin) if(MSVC) @@ -1004,14 +1001,14 @@ foreach(unit_test ${unit_test_PROGRAMS}) endif() endforeach() -parse_makefile_for_scripts(unit_tests_SUITES "UNIT_TESTS_SUITES" "") +parse_makefile_for_scripts(clar_test_SUITES "CLAR_TEST_SUITES" "") set(clar_decls "") set(clar_cbs "") set(clar_cbs_count 0) set(clar_suites "static struct clar_suite _clar_suites[] = {\n") -list(LENGTH unit_tests_SUITES clar_suites_count) -foreach(suite ${unit_tests_SUITES}) +list(LENGTH clar_test_SUITES clar_suites_count) +foreach(suite ${clar_test_SUITES}) file(STRINGS "${CMAKE_SOURCE_DIR}/t/unit-tests/${suite}.c" decls REGEX "^void test_${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*\\(void\\)$") @@ -1042,9 +1039,9 @@ string(APPEND clar_suites file(WRITE "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" "${clar_decls}") file(WRITE "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite" "${clar_decls}" "${clar_cbs}" "${clar_suites}") -list(TRANSFORM unit_tests_SUITES PREPEND "${CMAKE_SOURCE_DIR}/t/unit-tests/") -list(TRANSFORM unit_tests_SUITES APPEND ".c") -add_library(unit-tests-lib ${unit_tests_SUITES} "${CMAKE_SOURCE_DIR}/t/unit-tests/clar/clar.c") +list(TRANSFORM clar_test_SUITES PREPEND "${CMAKE_SOURCE_DIR}/t/unit-tests/") +list(TRANSFORM clar_test_SUITES APPEND ".c") +add_library(unit-tests-lib ${clar_test_SUITES} "${CMAKE_SOURCE_DIR}/t/unit-tests/clar/clar.c") target_include_directories(unit-tests-lib PRIVATE "${CMAKE_SOURCE_DIR}/t/unit-tests") add_executable(unit-tests "${CMAKE_SOURCE_DIR}/t/unit-tests/unit-test.c") target_link_libraries(unit-tests unit-tests-lib common-main) @@ -1062,7 +1059,7 @@ parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS") add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c) list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/") -add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES}) +add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES}) target_link_libraries(test-tool test-lib common-main) set_target_properties(test-fake-ssh test-tool diff --git a/credential.c b/credential.c index ee46351ce0..6dea3859ec 100644 --- a/credential.c +++ b/credential.c @@ -13,6 +13,8 @@ #include "strbuf.h" #include "urlmatch.h" #include "git-compat-util.h" +#include "trace2.h" +#include "repository.h" void credential_init(struct credential *c) { @@ -251,14 +253,36 @@ static char *credential_ask_one(const char *what, struct credential *c, return xstrdup(r); } -static void credential_getpass(struct credential *c) +static int credential_getpass(struct credential *c) { + int interactive; + char *value; + if (!git_config_get_maybe_bool("credential.interactive", &interactive) && + !interactive) { + trace2_data_intmax("credential", the_repository, + "interactive/skipped", 1); + return -1; + } + if (!git_config_get_string("credential.interactive", &value)) { + int same = !strcmp(value, "never"); + free(value); + if (same) { + trace2_data_intmax("credential", the_repository, + "interactive/skipped", 1); + return -1; + } + } + + trace2_region_enter("credential", "interactive", the_repository); if (!c->username) c->username = credential_ask_one("Username", c, PROMPT_ASKPASS|PROMPT_ECHO); if (!c->password) c->password = credential_ask_one("Password", c, PROMPT_ASKPASS); + trace2_region_leave("credential", "interactive", the_repository); + + return 0; } int credential_has_capability(const struct credential_capability *capa, @@ -501,8 +525,8 @@ void credential_fill(struct credential *c, int all_capabilities) c->helpers.items[i].string); } - credential_getpass(c); - if (!c->username && !c->password && !c->credential) + if (credential_getpass(c) || + (!c->username && !c->password && !c->credential)) die("unable to get password from user"); } @@ -443,8 +443,10 @@ int git_diff_ui_config(const char *var, const char *value, } if (!strcmp(var, "diff.wordregex")) return git_config_string(&diff_word_regex_cfg, var, value); - if (!strcmp(var, "diff.orderfile")) + if (!strcmp(var, "diff.orderfile")) { + FREE_AND_NULL(diff_order_file_cfg); return git_config_pathname(&diff_order_file_cfg, var, value); + } if (!strcmp(var, "diff.ignoresubmodules")) { if (!value) @@ -3675,6 +3677,7 @@ static void builtin_diff(const char *name_a, emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES, sb.buf, sb.len, 0); strbuf_release(&sb); + o->found_changes = 1; goto free_ab_and_return; } if (fill_mmfile(o->repo, &mf1, one) < 0 || @@ -4777,7 +4780,7 @@ void repo_diff_setup(struct repository *r, struct diff_options *options) if (diff_indent_heuristic) DIFF_XDL_SET(options, INDENT_HEURISTIC); - options->orderfile = diff_order_file_cfg; + options->orderfile = xstrdup_or_null(diff_order_file_cfg); if (!options->flags.ignore_submodule_set) options->flags.ignore_untracked_in_submodules = 1; @@ -6729,6 +6732,7 @@ void diff_free(struct diff_options *options) FREE_AND_NULL(options->objfind); } + FREE_AND_NULL(options->orderfile); for (size_t i = 0; i < options->anchors_nr; i++) free(options->anchors[i]); FREE_AND_NULL(options->anchors); @@ -235,7 +235,7 @@ enum diff_submodule_format { * diffcore library with. */ struct diff_options { - const char *orderfile; + char *orderfile; /* * "--rotate-to=<file>" would start showing at <file> and when diff --git a/diffcore-break.c b/diffcore-break.c index 831b66b5c3..02735f80c6 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -266,8 +266,8 @@ static void merge_broken(struct diff_filepair *p, * in the resulting tree. */ d->one->rename_used++; - diff_free_filespec_data(d->two); - diff_free_filespec_data(c->one); + free_filespec(d->two); + free_filespec(c->one); free(d); free(c); } diff --git a/diffcore-order.c b/diffcore-order.c index e7d20ebd2d..912513d3e6 100644 --- a/diffcore-order.c +++ b/diffcore-order.c @@ -14,8 +14,7 @@ static void prepare_order(const char *orderfile) { int cnt, pass; struct strbuf sb = STRBUF_INIT; - void *map; - char *cp, *endp; + const char *cp, *endp; ssize_t sz; if (order) @@ -24,14 +23,13 @@ static void prepare_order(const char *orderfile) sz = strbuf_read_file(&sb, orderfile, 0); if (sz < 0) die_errno(_("failed to read orderfile '%s'"), orderfile); - map = strbuf_detach(&sb, NULL); - endp = (char *) map + sz; + endp = sb.buf + sz; for (pass = 0; pass < 2; pass++) { cnt = 0; - cp = map; + cp = sb.buf; while (cp < endp) { - char *ep; + const char *ep; for (ep = cp; ep < endp && *ep != '\n'; ep++) ; /* cp to ep has one line */ @@ -40,12 +38,7 @@ static void prepare_order(const char *orderfile) else if (pass == 0) cnt++; else { - if (*ep == '\n') { - *ep = 0; - order[cnt] = cp; - } else { - order[cnt] = xmemdupz(cp, ep - cp); - } + order[cnt] = xmemdupz(cp, ep - cp); cnt++; } if (ep < endp) @@ -57,6 +50,8 @@ static void prepare_order(const char *orderfile) ALLOC_ARRAY(order, cnt); } } + + strbuf_release(&sb); } static int match_order(const char *path) diff --git a/diffcore-rename.c b/diffcore-rename.c index fab45b10d7..3d6826baa3 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -933,7 +933,7 @@ static int find_basename_matches(struct diff_options *options, * spend more cycles to find similarities between files, so it may * be less likely that this heuristic is wanted. If someone is * doing break detection, that means they do not want filename - * similarity to imply any form of content similiarity, and thus + * similarity to imply any form of content similarity, and thus * this heuristic would definitely be incompatible. */ @@ -1534,7 +1534,7 @@ void diffcore_rename_extended(struct diff_options *options, * - remove ones not found in relevant_sources * and * - remove ones in relevant_sources which are needed only - * for directory renames IF no ancestory directory + * for directory renames IF no ancestry directory * actually needs to know any more individual path * renames under them */ @@ -2136,8 +2136,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, */ state = path_none; } else { - int i; - for (i = old_ignored_nr + 1; i<dir->ignored_nr; ++i) + for (int i = old_ignored_nr; i < dir->ignored_nr; i++) FREE_AND_NULL(dir->ignored[i]); dir->ignored_nr = old_ignored_nr; } @@ -2149,8 +2148,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir, */ if ((dir->flags & DIR_SHOW_IGNORED_TOO) && !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) { - int i; - for (i = old_untracked_nr + 1; i<dir->nr; ++i) + for (int i = old_untracked_nr; i < dir->nr; i++) FREE_AND_NULL(dir->entries[i]); dir->nr = old_untracked_nr; } diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index e818583420..a6587a8972 100644 --- a/fsmonitor-settings.c +++ b/fsmonitor-settings.c @@ -7,7 +7,7 @@ #include "fsmonitor-path-utils.h" /* - * We keep this structure defintion private and have getters + * We keep this structure definition private and have getters * for all fields so that we can lazy load it as needed. */ struct fsmonitor_settings { diff --git a/git-instaweb.sh b/git-instaweb.sh index 994431c887..8dbe21d588 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -612,7 +612,7 @@ python_conf() { ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/" # generate a standalone 'python http.server' script in $fqgitdir/gitweb - # This asumes that python is in user's $PATH + # This assumes that python is in user's $PATH # This script is Python 2 and 3 compatible cat > "$fqgitdir/gitweb/gitweb.py" <<EOF #!/usr/bin/env python @@ -54,7 +54,7 @@ import time import zipfile import zlib -# On python2.7 where raw_input() and input() are both availble, +# On python2.7 where raw_input() and input() are both available, # we want raw_input's semantics, but aliased to input for python3 # compatibility # support basestring in python3 @@ -1804,7 +1804,7 @@ class P4Submit(Command, P4UserMap): status from the script will abort the process. The purpose of the hook is to edit the message file in place, and it is not - supressed by the `--no-verify` option. This hook is called even if + suppressed by the `--no-verify` option. This hook is called even if `--prepare-p4-only` is set. The `p4-changelist` hook is executed after the changelist message has been @@ -711,6 +711,7 @@ static void strip_extension(const char **argv) static void handle_builtin(int argc, const char **argv) { struct strvec args = STRVEC_INIT; + const char **argv_copy = NULL; const char *cmd; struct cmd_struct *builtin; @@ -731,13 +732,28 @@ static void handle_builtin(int argc, const char **argv) } argc++; - argv = args.v; + + /* + * `run_builtin()` will modify the argv array, so we need to + * create a shallow copy such that we can free all of its + * strings. + */ + CALLOC_ARRAY(argv_copy, argc + 1); + COPY_ARRAY(argv_copy, args.v, argc); + + argv = argv_copy; } builtin = get_builtin(cmd); - if (builtin) - exit(run_builtin(builtin, argc, argv, the_repository)); + if (builtin) { + int ret = run_builtin(builtin, argc, argv, the_repository); + strvec_clear(&args); + free(argv_copy); + exit(ret); + } + strvec_clear(&args); + free(argv_copy); } static void execv_dashed_external(const char **argv) diff --git a/gpg-interface.c b/gpg-interface.c index cf6126b5aa..07335987a6 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -400,7 +400,7 @@ static void parse_ssh_output(struct signature_check *sigc) * Note that "PRINCIPAL" can contain whitespace, "RSA" and * "SHA256" part could be a different token that names of * the algorithms used, and "FINGERPRINT" is a hexadecimal - * string. By finding the last occurence of " with ", we can + * string. By finding the last occurrence of " with ", we can * reliably parse out the PRINCIPAL. */ sigc->result = 'B'; @@ -843,11 +843,11 @@ static void free_grep_pat(struct grep_pat *pattern) free_pcre2_pattern(p); else regfree(&p->regexp); - free(p->pattern); break; default: break; } + free(p->pattern); free(p); } } @@ -1708,7 +1708,7 @@ void run_active_slot(struct active_request_slot *slot) * The value of slot->finished we set before the loop was used * to set our "finished" variable when our request completed. * - * 1. The slot may not have been reused for another requst + * 1. The slot may not have been reused for another request * yet, in which case it still has &finished. * * 2. The slot may already be in-use to serve another request, diff --git a/merge-ll.c b/merge-ll.c index badb6dea57..8e63071922 100644 --- a/merge-ll.c +++ b/merge-ll.c @@ -334,7 +334,7 @@ static int read_merge_config(const char *var, const char *value, * %X - the revision for our version * %Y - the revision for their version * - * If the file is not named indentically in all versions, then each + * If the file is not named identically in all versions, then each * revision is joined with the corresponding path, separated by a colon. * The external merge driver should write the results in the * file named by %A, and signal that it has done with zero exit diff --git a/merge-ort.c b/merge-ort.c index 0ed3cd06b1..8b81153e8f 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -1147,7 +1147,7 @@ static void collect_rename_info(struct merge_options *opt, * Update dir_rename_mask (determines ignore-rename-source validity) * * dir_rename_mask helps us keep track of when directory rename - * detection may be relevant. Basically, whenver a directory is + * detection may be relevant. Basically, whenever a directory is * removed on one side of history, and a file is added to that * directory on the other side of history, directory rename * detection is relevant (meaning we have to detect renames for all @@ -3837,7 +3837,7 @@ static int write_completed_directory(struct merge_options *opt, * src/moduleB 2 * * which is used to know that xtract.c & token.txt are from the - * toplevel dirctory, while umm.c & stuff.h & baz.c are from the + * toplevel directory, while umm.c & stuff.h & baz.c are from the * src/moduleB directory. Again, following the example above, * once we need to process src/moduleB, then info->offsets is * updated to diff --git a/object-file.c b/object-file.c index 968da27cd4..7ac9533ab1 100644 --- a/object-file.c +++ b/object-file.c @@ -2308,7 +2308,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, /* * Common steps for write_loose_object and stream_loose_object to - * end writing loose oject: + * end writing loose object: * * - End the compression of zlib stream. * - Get the calculated oid. diff --git a/parse-options.c b/parse-options.c index 30b9e68f8a..33bfba0ed4 100644 --- a/parse-options.c +++ b/parse-options.c @@ -60,12 +60,12 @@ static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p, return 0; } -static void fix_filename(const char *prefix, char **file) +static char *fix_filename(const char *prefix, const char *file) { if (!file || !*file) - ; /* leave as NULL */ + return NULL; else - *file = prefix_filename_except_for_dash(prefix, *file); + return prefix_filename_except_for_dash(prefix, file); } static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, @@ -129,18 +129,24 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return 0; case OPTION_FILENAME: + { + const char *value; + + FREE_AND_NULL(*(char **)opt->value); + err = 0; + if (unset) - *(const char **)opt->value = NULL; + value = NULL; else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - *(const char **)opt->value = (const char *)opt->defval; + value = (const char *) opt->defval; else - err = get_arg(p, opt, flags, (const char **)opt->value); + err = get_arg(p, opt, flags, &value); if (!err) - fix_filename(p->prefix, (char **)opt->value); + *(char **)opt->value = fix_filename(p->prefix, value); return err; - + } case OPTION_CALLBACK: { const char *p_arg = NULL; @@ -2205,7 +2205,7 @@ static void strbuf_add_tabexpand(struct strbuf *sb, struct grep_opt *opt, } /* - * pp_handle_indent() prints out the intendation, and + * pp_handle_indent() prints out the indentation, and * the whole line (without the final newline), after * de-tabifying. */ diff --git a/promisor-remote.c b/promisor-remote.c index 317e1b127f..9345ae3db2 100644 --- a/promisor-remote.c +++ b/promisor-remote.c @@ -154,6 +154,7 @@ static int promisor_remote_config(const char *var, const char *value, if (!r) return 0; + FREE_AND_NULL(r->partial_clone_filter); return git_config_string(&r->partial_clone_filter, var, value); } @@ -189,6 +190,7 @@ void promisor_remote_clear(struct promisor_remote_config *config) { while (config->promisors) { struct promisor_remote *r = config->promisors; + free(r->partial_clone_filter); config->promisors = config->promisors->next; free(r); } diff --git a/read-cache-ll.h b/read-cache-ll.h index e0e39607ef..b5d11d07a8 100644 --- a/read-cache-ll.h +++ b/read-cache-ll.h @@ -151,7 +151,7 @@ enum sparse_index_mode { /* * The index has already been collapsed to sparse directories - * whereever possible. + * wherever possible. */ INDEX_COLLAPSED, @@ -11,7 +11,7 @@ * The callers that care if (any) rebase is requested should say * if (REBASE_TRUE <= rebase_parse_value(string)) * - * The callers that want to differenciate an unrecognised value and + * The callers that want to differentiate an unrecognised value and * false can do so by treating _INVALID and _FALSE differently. */ enum rebase_type rebase_parse_value(const char *value) diff --git a/ref-filter.c b/ref-filter.c index fce96d7739..dd195007ce 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2385,7 +2385,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) CALLOC_ARRAY(ref->value, used_atom_cnt); /** - * NEEDSWORK: The following code might be unncessary if all codepaths + * NEEDSWORK: The following code might be unnecessary if all codepaths * that call populate_value() populates the symref member of ref_array_item * like in apply_ref_filter(). Currently pretty_print_ref() is the only codepath * that calls populate_value() without first populating symref. @@ -1518,6 +1518,19 @@ const char **hidden_refs_to_excludes(const struct strvec *hide_refs) return hide_refs->v; } +const char **get_namespaced_exclude_patterns(const char **exclude_patterns, + const char *namespace, + struct strvec *out) +{ + if (!namespace || !*namespace || !exclude_patterns || !*exclude_patterns) + return exclude_patterns; + + for (size_t i = 0; exclude_patterns[i]; i++) + strvec_pushf(out, "%s%s", namespace, exclude_patterns[i]); + + return out->v; +} + const char *find_descendant_ref(const char *dirname, const struct string_list *extras, const struct string_list *skip) @@ -1635,11 +1648,19 @@ int refs_for_each_namespaced_ref(struct ref_store *refs, const char **exclude_patterns, each_ref_fn fn, void *cb_data) { - struct strbuf buf = STRBUF_INIT; + struct strvec namespaced_exclude_patterns = STRVEC_INIT; + struct strbuf prefix = STRBUF_INIT; int ret; - strbuf_addf(&buf, "%srefs/", get_git_namespace()); - ret = do_for_each_ref(refs, buf.buf, exclude_patterns, fn, 0, 0, cb_data); - strbuf_release(&buf); + + exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns, + get_git_namespace(), + &namespaced_exclude_patterns); + + strbuf_addf(&prefix, "%srefs/", get_git_namespace()); + ret = do_for_each_ref(refs, prefix.buf, exclude_patterns, fn, 0, 0, cb_data); + + strvec_clear(&namespaced_exclude_patterns); + strbuf_release(&prefix); return ret; } @@ -1720,6 +1741,7 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store, const char **exclude_patterns, each_ref_fn fn, void *cb_data) { + struct strvec namespaced_exclude_patterns = STRVEC_INIT; struct string_list prefixes = STRING_LIST_INIT_DUP; struct string_list_item *prefix; struct strbuf buf = STRBUF_INIT; @@ -1731,6 +1753,10 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store, strbuf_addstr(&buf, namespace); namespace_len = buf.len; + exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns, + namespace, + &namespaced_exclude_patterns); + for_each_string_list_item(prefix, &prefixes) { strbuf_addstr(&buf, prefix->string); ret = refs_for_each_fullref_in(ref_store, buf.buf, @@ -1740,6 +1766,7 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store, strbuf_setlen(&buf, namespace_len); } + strvec_clear(&namespaced_exclude_patterns); string_list_clear(&prefixes, 0); strbuf_release(&buf); return ret; @@ -17,7 +17,7 @@ enum ref_storage_format ref_storage_format_by_name(const char *name); const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format); /* - * Resolve a reference, recursively following symbolic refererences. + * Resolve a reference, recursively following symbolic references. * * Return the name of the non-symbolic reference that ultimately pointed * at the resolved object name. The return value, if not NULL, is a @@ -490,7 +490,7 @@ int refs_delete_reflog(struct ref_store *refs, const char *refname); * from UTC. Its absolute value is formed by multiplying the hour * part by 100 and adding the minute part. For example, 1 hour ahead * of UTC, CET == "+0100", is represented as positive one hundred (not - * postiive sixty). + * positive sixty). * * The msg parameter is a single complete line; a reflog message given * to refs_delete_ref, refs_update_ref, etc. is returned to the @@ -861,6 +861,15 @@ int ref_is_hidden(const char *, const char *, const struct strvec *); */ const char **hidden_refs_to_excludes(const struct strvec *hide_refs); +/* + * Prefix all exclude patterns with the namespace, if any. This is required + * because exclude patterns apply to the stripped reference name, not the full + * reference name with the namespace. + */ +const char **get_namespaced_exclude_patterns(const char **exclude_patterns, + const char *namespace, + struct strvec *out); + /* Is this a per-worktree ref living in the refs/ namespace? */ int is_per_worktree_ref(const char *refname); @@ -988,7 +997,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt); /* * Some of the names specified by refs have special meaning to Git. - * Organize these namespaces in a comon 'ref_namespace' array for + * Organize these namespaces in a common 'ref_namespace' array for * reference from multiple places in the codebase. */ diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 043e19439f..3c96fbf66f 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -22,6 +22,7 @@ #include "../repo-settings.h" #include "../setup.h" #include "../strmap.h" +#include "../trace2.h" #include "parse.h" #include "refs-internal.h" @@ -259,6 +260,13 @@ static int reftable_be_config(const char *var, const char *value, if (factor > UINT8_MAX) die("reftable geometric factor cannot exceed %u", (unsigned)UINT8_MAX); opts->auto_compaction_factor = factor; + } else if (!strcmp(var, "reftable.locktimeout")) { + int64_t lock_timeout = git_config_int64(var, value, ctx->kvi); + if (lock_timeout > LONG_MAX) + die("reftable lock timeout cannot exceed %"PRIdMAX, (intmax_t)LONG_MAX); + if (lock_timeout < 0 && lock_timeout != -1) + die("reftable lock timeout does not support negative values other than -1"); + opts->lock_timeout_ms = lock_timeout; } return 0; @@ -285,6 +293,7 @@ static struct ref_store *reftable_be_init(struct repository *repo, refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask); refs->write_options.disable_auto_compact = !git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1); + refs->write_options.lock_timeout_ms = 100; git_config(reftable_be_config, &refs->write_options); @@ -451,10 +460,81 @@ struct reftable_ref_iterator { const char *prefix; size_t prefix_len; + char **exclude_patterns; + size_t exclude_patterns_index; + size_t exclude_patterns_strlen; unsigned int flags; int err; }; +/* + * Handle exclude patterns. Returns either `1`, which tells the caller that the + * current reference shall not be shown. Or `0`, which indicates that it should + * be shown. + */ +static int should_exclude_current_ref(struct reftable_ref_iterator *iter) +{ + while (iter->exclude_patterns[iter->exclude_patterns_index]) { + const char *pattern = iter->exclude_patterns[iter->exclude_patterns_index]; + char *ref_after_pattern; + int cmp; + + /* + * Lazily cache the pattern length so that we don't have to + * recompute it every time this function is called. + */ + if (!iter->exclude_patterns_strlen) + iter->exclude_patterns_strlen = strlen(pattern); + + /* + * When the reference name is lexicographically bigger than the + * current exclude pattern we know that it won't ever match any + * of the following references, either. We thus advance to the + * next pattern and re-check whether it matches. + * + * Otherwise, if it's smaller, then we do not have a match and + * thus want to show the current reference. + */ + cmp = strncmp(iter->ref.refname, pattern, + iter->exclude_patterns_strlen); + if (cmp > 0) { + iter->exclude_patterns_index++; + iter->exclude_patterns_strlen = 0; + continue; + } + if (cmp < 0) + return 0; + + /* + * The reference shares a prefix with the exclude pattern and + * shall thus be omitted. We skip all references that match the + * pattern by seeking to the first reference after the block of + * matches. + * + * This is done by appending the highest possible character to + * the pattern. Consequently, all references that have the + * pattern as prefix and whose suffix starts with anything in + * the range [0x00, 0xfe] are skipped. And given that 0xff is a + * non-printable character that shouldn't ever be in a ref name, + * we'd not yield any such record, either. + * + * Note that the seeked-to reference may also be excluded. This + * is not handled here though, but the caller is expected to + * loop and re-verify the next reference for us. + */ + ref_after_pattern = xstrfmt("%s%c", pattern, 0xff); + iter->err = reftable_iterator_seek_ref(&iter->iter, ref_after_pattern); + iter->exclude_patterns_index++; + iter->exclude_patterns_strlen = 0; + trace2_counter_add(TRACE2_COUNTER_ID_REFTABLE_RESEEKS, 1); + + free(ref_after_pattern); + return 1; + } + + return 0; +} + static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) { struct reftable_ref_iterator *iter = @@ -485,6 +565,9 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) break; } + if (iter->exclude_patterns && should_exclude_current_ref(iter)) + continue; + if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY && parse_worktree_ref(iter->ref.refname, NULL, NULL, NULL) != REF_WORKTREE_CURRENT) @@ -574,6 +657,11 @@ static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator) (struct reftable_ref_iterator *)ref_iterator; reftable_ref_record_release(&iter->ref); reftable_iterator_destroy(&iter->iter); + if (iter->exclude_patterns) { + for (size_t i = 0; iter->exclude_patterns[i]; i++) + free(iter->exclude_patterns[i]); + free(iter->exclude_patterns); + } free(iter); return ITER_DONE; } @@ -584,9 +672,53 @@ static struct ref_iterator_vtable reftable_ref_iterator_vtable = { .abort = reftable_ref_iterator_abort }; +static int qsort_strcmp(const void *va, const void *vb) +{ + const char *a = *(const char **)va; + const char *b = *(const char **)vb; + return strcmp(a, b); +} + +static char **filter_exclude_patterns(const char **exclude_patterns) +{ + size_t filtered_size = 0, filtered_alloc = 0; + char **filtered = NULL; + + if (!exclude_patterns) + return NULL; + + for (size_t i = 0; ; i++) { + const char *exclude_pattern = exclude_patterns[i]; + int has_glob = 0; + + if (!exclude_pattern) + break; + + for (const char *p = exclude_pattern; *p; p++) { + has_glob = is_glob_special(*p); + if (has_glob) + break; + } + if (has_glob) + continue; + + ALLOC_GROW(filtered, filtered_size + 1, filtered_alloc); + filtered[filtered_size++] = xstrdup(exclude_pattern); + } + + if (filtered_size) { + QSORT(filtered, filtered_size, qsort_strcmp); + ALLOC_GROW(filtered, filtered_size + 1, filtered_alloc); + filtered[filtered_size++] = NULL; + } + + return filtered; +} + static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_store *refs, struct reftable_stack *stack, const char *prefix, + const char **exclude_patterns, int flags) { struct reftable_ref_iterator *iter; @@ -599,6 +731,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_ iter->base.oid = &iter->oid; iter->flags = flags; iter->refs = refs; + iter->exclude_patterns = filter_exclude_patterns(exclude_patterns); ret = refs->err; if (ret) @@ -620,7 +753,7 @@ done: static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store, const char *prefix, - const char **exclude_patterns UNUSED, + const char **exclude_patterns, unsigned int flags) { struct reftable_ref_iterator *main_iter, *worktree_iter; @@ -631,7 +764,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto required_flags |= REF_STORE_ODB; refs = reftable_be_downcast(ref_store, required_flags, "ref_iterator_begin"); - main_iter = ref_iterator_for_stack(refs, refs->main_stack, prefix, flags); + main_iter = ref_iterator_for_stack(refs, refs->main_stack, prefix, + exclude_patterns, flags); /* * The worktree stack is only set when we're in an actual worktree @@ -645,7 +779,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto * Otherwise we merge both the common and the per-worktree refs into a * single iterator. */ - worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags); + worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, + exclude_patterns, flags); return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base, ref_iterator_select, NULL); } @@ -766,7 +901,8 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out, if (ret) return ret; - ret = reftable_stack_new_addition(&addition, stack); + ret = reftable_stack_new_addition(&addition, stack, + REFTABLE_STACK_NEW_ADDITION_RELOAD); if (ret) { if (ret == REFTABLE_LOCK_ERROR) strbuf_addstr(err, "cannot lock references"); @@ -2203,7 +2339,7 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, if (ret < 0) goto done; - ret = reftable_stack_new_addition(&add, stack); + ret = reftable_stack_new_addition(&add, stack, 0); if (ret < 0) goto done; diff --git a/reftable/reader.c b/reftable/reader.c index f877099087..6494ce2e32 100644 --- a/reftable/reader.c +++ b/reftable/reader.c @@ -328,6 +328,7 @@ static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ) ti->typ = block_reader_type(&ti->br); ti->block_off = off; block_iter_seek_start(&ti->bi, &ti->br); + ti->is_finished = 0; return 0; } diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h index f4f8cabc7f..6370fe45dd 100644 --- a/reftable/reftable-stack.h +++ b/reftable/reftable-stack.h @@ -37,12 +37,21 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st); /* holds a transaction to add tables at the top of a stack. */ struct reftable_addition; +enum { + /* + * Reload the stack when the stack is out-of-date after locking it. + */ + REFTABLE_STACK_NEW_ADDITION_RELOAD = (1 << 0), +}; + /* * returns a new transaction to add reftables to the given stack. As a side - * effect, the ref database is locked. + * effect, the ref database is locked. Accepts REFTABLE_STACK_NEW_ADDITION_* + * flags. */ int reftable_stack_new_addition(struct reftable_addition **dest, - struct reftable_stack *st); + struct reftable_stack *st, + unsigned int flags); /* Adds a reftable to transaction. */ int reftable_addition_add(struct reftable_addition *add, diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h index 189b1f4144..f5e25cfda1 100644 --- a/reftable/reftable-writer.h +++ b/reftable/reftable-writer.h @@ -51,6 +51,17 @@ struct reftable_write_options { * tables to compact. Defaults to 2 if unset. */ uint8_t auto_compaction_factor; + + /* + * The number of milliseconds to wait when trying to lock "tables.list". + * Note that this does not apply to locking individual tables, as these + * should only ever be locked when already holding the "tables.list" + * lock. + * + * Passing 0 will fail immediately when the file is locked, passing a + * negative value will cause us to block indefinitely. + */ + long lock_timeout_ms; }; /* reftable_block_stats holds statistics for a single block type */ diff --git a/reftable/stack.c b/reftable/stack.c index ce0a35216b..84cf37a2ad 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -596,15 +596,18 @@ struct reftable_addition { #define REFTABLE_ADDITION_INIT {0} static int reftable_stack_init_addition(struct reftable_addition *add, - struct reftable_stack *st) + struct reftable_stack *st, + unsigned int flags) { struct strbuf lock_file_name = STRBUF_INIT; int err; add->stack = st; - err = hold_lock_file_for_update(&add->tables_list_lock, st->list_file, - LOCK_NO_DEREF); + err = hold_lock_file_for_update_timeout(&add->tables_list_lock, + st->list_file, + LOCK_NO_DEREF, + st->opts.lock_timeout_ms); if (err < 0) { if (errno == EEXIST) { err = REFTABLE_LOCK_ERROR; @@ -624,6 +627,11 @@ static int reftable_stack_init_addition(struct reftable_addition *add, err = stack_uptodate(st); if (err < 0) goto done; + if (err > 0 && flags & REFTABLE_STACK_NEW_ADDITION_RELOAD) { + err = reftable_stack_reload_maybe_reuse(add->stack, 1); + if (err) + goto done; + } if (err > 0) { err = REFTABLE_OUTDATED_ERROR; goto done; @@ -631,9 +639,8 @@ static int reftable_stack_init_addition(struct reftable_addition *add, add->next_update_index = reftable_stack_next_update_index(st); done: - if (err) { + if (err) reftable_addition_close(add); - } strbuf_release(&lock_file_name); return err; } @@ -737,13 +744,14 @@ done: } int reftable_stack_new_addition(struct reftable_addition **dest, - struct reftable_stack *st) + struct reftable_stack *st, + unsigned int flags) { int err = 0; struct reftable_addition empty = REFTABLE_ADDITION_INIT; REFTABLE_CALLOC_ARRAY(*dest, 1); **dest = empty; - err = reftable_stack_init_addition(*dest, st); + err = reftable_stack_init_addition(*dest, st, flags); if (err) { reftable_free(*dest); *dest = NULL; @@ -757,7 +765,7 @@ static int stack_try_add(struct reftable_stack *st, void *arg) { struct reftable_addition add = REFTABLE_ADDITION_INIT; - int err = reftable_stack_init_addition(&add, st); + int err = reftable_stack_init_addition(&add, st, 0); if (err < 0) goto done; @@ -1056,8 +1064,10 @@ static int stack_compact_range(struct reftable_stack *st, * Hold the lock so that we can read "tables.list" and lock all tables * which are part of the user-specified range. */ - err = hold_lock_file_for_update(&tables_list_lock, st->list_file, - LOCK_NO_DEREF); + err = hold_lock_file_for_update_timeout(&tables_list_lock, + st->list_file, + LOCK_NO_DEREF, + st->opts.lock_timeout_ms); if (err < 0) { if (errno == EEXIST) err = REFTABLE_LOCK_ERROR; @@ -1156,8 +1166,10 @@ static int stack_compact_range(struct reftable_stack *st, * "tables.list". We'll then replace the compacted range of tables with * the new table. */ - err = hold_lock_file_for_update(&tables_list_lock, st->list_file, - LOCK_NO_DEREF); + err = hold_lock_file_for_update_timeout(&tables_list_lock, + st->list_file, + LOCK_NO_DEREF, + st->opts.lock_timeout_ms); if (err < 0) { if (errno == EEXIST) err = REFTABLE_LOCK_ERROR; @@ -1602,7 +1614,7 @@ static int reftable_stack_clean_locked(struct reftable_stack *st) int reftable_stack_clean(struct reftable_stack *st) { struct reftable_addition *add = NULL; - int err = reftable_stack_new_addition(&add, st); + int err = reftable_stack_new_addition(&add, st, 0); if (err < 0) { goto done; } diff --git a/revision.c b/revision.c index 2d7ad2bddf..e79f39e555 100644 --- a/revision.c +++ b/revision.c @@ -1071,7 +1071,11 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) ts->treesame[nth_parent] = 1; continue; } + + free_commit_list(parent->next); parent->next = NULL; + while (commit->parents != parent) + pop_commit(&commit->parents); commit->parents = parent; /* @@ -1103,6 +1107,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) die("cannot simplify commit %s (invalid %s)", oid_to_hex(&commit->object.oid), oid_to_hex(&p->object.oid)); + free_commit_list(p->parents); p->parents = NULL; } /* fallthrough */ diff --git a/revision.h b/revision.h index 0e470d1df1..71e984c452 100644 --- a/revision.h +++ b/revision.h @@ -549,7 +549,7 @@ int rewrite_parents(struct rev_info *revs, * The log machinery saves the original parent list so that * get_saved_parents() can later tell what the real parents of the * commits are, when commit->parents has been modified by history - * simpification. + * simplification. * * get_saved_parents() will transparently return commit->parents if * history simplification is off. diff --git a/run-command.h b/run-command.h index 03e7222d8b..0df25e445f 100644 --- a/run-command.h +++ b/run-command.h @@ -535,7 +535,7 @@ enum start_bg_result { /* timeout expired waiting for child to become "ready" */ SBGR_TIMEOUT, - /* child process exited or was signalled before becomming "ready" */ + /* child process exited or was signalled before becoming "ready" */ SBGR_DIED, }; @@ -733,6 +733,9 @@ static int cmd_reconfigure(int argc, const char **argv) the_repository = old_repo; + if (toggle_maintenance(1) >= 0) + succeeded = 1; + loop_end: if (!succeeded) { res = -1; @@ -524,7 +524,7 @@ static void setup_original_cwd(void) * directory we inherited from our parent process, which is a * directory we want to avoid removing. * - * For convience, we would like to have the path relative to the + * For convenience, we would like to have the path relative to the * worktree instead of an absolute path. * * Yes, startup_info->original_cwd is usually the same as 'prefix', diff --git a/sideband.c b/sideband.c index 4e816be4e9..02805573fa 100644 --- a/sideband.c +++ b/sideband.c @@ -191,7 +191,7 @@ int demultiplex_sideband(const char *me, int status, int linelen = brk - b; /* - * For message accross packet boundary, there would have + * For message across packet boundary, there would have * a nonempty "scratch" buffer from last call of this * function, and there may have a leading CR/LF in "buf". * For this case we should add a clear-to-eol suffix to diff --git a/sparse-index.c b/sparse-index.c index 542ca5f411..3d7f2164e2 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -21,9 +21,10 @@ * advice for advice.sparseIndexExpanded when expanding a sparse index to a full * one. However, this is sometimes done on purpose, such as in the sparse-checkout * builtin, even when index.sparse=false. This may be disabled in - * convert_to_sparse(). + * convert_to_sparse() or by commands that know they will lead to a full + * expansion, but this message is not actionable. */ -static int give_advice_on_expansion = 1; +int give_advice_on_expansion = 1; #define ADVICE_MSG \ "The sparse index is expanding to a full index, a slow operation.\n" \ "Your working directory likely has contents that are outside of\n" \ diff --git a/sparse-index.h b/sparse-index.h index a16f3e67d7..727034be7c 100644 --- a/sparse-index.h +++ b/sparse-index.h @@ -1,6 +1,13 @@ #ifndef SPARSE_INDEX_H__ #define SPARSE_INDEX_H__ +/* + * If performing an operation where the index is supposed to expand to a + * full index, then disable the advice message by setting this global to + * zero. + */ +extern int give_advice_on_expansion; + struct index_state; #define SPARSE_INDEX_MEMORY_ONLY (1 << 0) int is_sparse_index_allowed(struct index_state *istate, int flags); diff --git a/submodule-config.c b/submodule-config.c index c8f2bb2bdd..471637a725 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -95,7 +95,7 @@ static void free_one_config(struct submodule_entry *entry) free((void *) entry->config->branch); free((void *) entry->config->url); free((void *) entry->config->ignore); - free((void *) entry->config->update_strategy.command); + submodule_update_strategy_release(&entry->config->update_strategy); free(entry->config); } diff --git a/submodule.c b/submodule.c index 4e71ac0dfd..74d5766f07 100644 --- a/submodule.c +++ b/submodule.c @@ -175,11 +175,11 @@ void stage_updated_gitmodules(struct index_state *istate) die(_("staging updated .gitmodules failed")); } -static struct string_list added_submodule_odb_paths = STRING_LIST_INIT_NODUP; +static struct string_list added_submodule_odb_paths = STRING_LIST_INIT_DUP; void add_submodule_odb_by_path(const char *path) { - string_list_insert(&added_submodule_odb_paths, xstrdup(path)); + string_list_insert(&added_submodule_odb_paths, path); } int register_all_submodule_odb_as_alternates(void) @@ -424,6 +424,11 @@ int parse_submodule_update_strategy(const char *value, return 0; } +void submodule_update_strategy_release(struct submodule_update_strategy *strategy) +{ + free((char *) strategy->command); +} + const char *submodule_update_type_to_string(enum submodule_update_type type) { switch (type) { diff --git a/submodule.h b/submodule.h index b50d29eba4..4deb1b5f84 100644 --- a/submodule.h +++ b/submodule.h @@ -41,6 +41,10 @@ struct submodule_update_strategy { .type = SM_UPDATE_UNSPECIFIED, \ } +int parse_submodule_update_strategy(const char *value, + struct submodule_update_strategy *dst); +void submodule_update_strategy_release(struct submodule_update_strategy *strategy); + int is_gitmodules_unmerged(struct index_state *istate); int is_writing_gitmodules_ok(void); int is_staging_gitmodules_ok(struct index_state *istate); @@ -70,8 +74,6 @@ void die_in_unpopulated_submodule(struct index_state *istate, void die_path_inside_submodule(struct index_state *istate, const struct pathspec *ps); enum submodule_update_type parse_submodule_update_type(const char *value); -int parse_submodule_update_strategy(const char *value, - struct submodule_update_strategy *dst); const char *submodule_update_type_to_string(enum submodule_update_type type); void handle_ignore_submodules_arg(struct diff_options *, const char *); void show_submodule_diff_summary(struct diff_options *o, const char *path, diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c index 6ca069ce63..6dce957153 100644 --- a/t/helper/test-submodule-nested-repo-config.c +++ b/t/helper/test-submodule-nested-repo-config.c @@ -29,6 +29,6 @@ int cmd__submodule_nested_repo_config(int argc, const char **argv) print_config_from_gitmodules(&subrepo, argv[2]); submodule_free(the_repository); - + repo_clear(&subrepo); return 0; } diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 1d273d91c2..9eae0d8356 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -2,6 +2,7 @@ test_description='help' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh configure_help () { diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index 37510c2b2a..2d951c8ceb 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh @@ -423,6 +423,64 @@ test_expect_success 'ref transaction: fails gracefully when auto compaction fail ) ' +test_expect_success 'ref transaction: timeout acquiring tables.list lock' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + >.git/reftable/tables.list.lock && + test_must_fail git update-ref refs/heads/branch HEAD 2>err && + test_grep "cannot lock references" err + ) +' + +test_expect_success 'ref transaction: retry acquiring tables.list lock' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + LOCK=.git/reftable/tables.list.lock && + >$LOCK && + { + ( sleep 1 && rm -f $LOCK ) & + } && + git -c reftable.lockTimeout=5000 update-ref refs/heads/branch HEAD + ) +' + +test_expect_success 'ref transaction: many concurrent writers' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + # Set a high timeout such that a busy CI machine will not abort + # early. 10 seconds should hopefully be ample of time to make + # this non-flaky. + git config set reftable.lockTimeout 10000 && + test_commit --no-tag initial && + + head=$(git rev-parse HEAD) && + for i in $(test_seq 100) + do + printf "%s commit\trefs/heads/branch-%s\n" "$head" "$i" || + return 1 + done >expect && + printf "%s commit\trefs/heads/main\n" "$head" >>expect && + + for i in $(test_seq 100) + do + { git update-ref refs/heads/branch-$i HEAD& } || + return 1 + done && + + wait && + git for-each-ref --sort=v:refname >actual && + test_cmp expect actual + ) +' + test_expect_success 'pack-refs: compacts tables' ' test_when_finished "rm -rf repo" && git init repo && diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index eb32da2a7f..6e230b5487 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -2355,7 +2355,10 @@ test_expect_success 'advice.sparseIndexExpanded' ' mkdir -p sparse-index/deep/deeper2/deepest && touch sparse-index/deep/deeper2/deepest/bogus && git -C sparse-index status 2>err && - grep "The sparse index is expanding to a full index" err + grep "The sparse index is expanding to a full index" err && + + git -C sparse-index sparse-checkout disable 2>err && + test_line_count = 0 err ' test_expect_success 'cat-file -p' ' diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index 5cde79ef8c..517d6c8693 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -357,4 +357,44 @@ test_expect_success 'include cycles are detected' ' grep "exceeded maximum include depth" stderr ' +test_expect_success 'onbranch with unborn branch' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + git config set includeIf.onbranch:"*".path config.inc && + git config set -f .git/config.inc foo.bar baz && + git config get foo.bar + ) +' + +test_expect_success 'onbranch with detached HEAD' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + git config set "includeIf.onbranch:*.path" config.inc && + git config set -f .git/config.inc foo.bar baz && + test_commit initial && + git switch --detach HEAD && + test_must_fail git config get foo.bar + ) +' + +test_expect_success 'onbranch without repository' ' + test_when_finished "rm -f .gitconfig config.inc" && + git config set -f .gitconfig "includeIf.onbranch:**.path" config.inc && + git config set -f config.inc foo.bar baz && + git config get foo.bar && + test_must_fail nongit git config get foo.bar +' + +test_expect_success 'onbranch without repository but explicit nonexistent Git directory' ' + test_when_finished "rm -f .gitconfig config.inc" && + git config set -f .gitconfig "includeIf.onbranch:**.path" config.inc && + git config set -f config.inc foo.bar baz && + git config get foo.bar && + test_must_fail nongit git --git-dir=nonexistent config get foo.bar +' + test_done diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh index be6c3f472c..49d28166da 100755 --- a/t/t1414-reflog-walk.sh +++ b/t/t1414-reflog-walk.sh @@ -4,6 +4,7 @@ test_description='various tests of reflog walk (log -g) behavior' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'set up some reflog entries' ' diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh index 1359574419..3256e4462f 100755 --- a/t/t1419-exclude-refs.sh +++ b/t/t1419-exclude-refs.sh @@ -8,12 +8,6 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh -if test_have_prereq !REFFILES -then - skip_all='skipping `git for-each-ref --exclude` tests; need files backend' - test_done -fi - for_each_ref__exclude () { GIT_TRACE2_PERF=1 test-tool ref-store main \ for-each-ref--exclude "$@" >actual.raw @@ -28,7 +22,14 @@ assert_jumps () { local nr="$1" local trace="$2" - grep -q "name:jumps_made value:$nr$" $trace + case "$GIT_DEFAULT_REF_FORMAT" in + files) + grep -q "name:jumps_made value:$nr$" $trace;; + reftable) + grep -q "name:reseeks_made value:$nr$" $trace;; + *) + BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";; + esac } assert_no_jumps () { @@ -89,7 +90,14 @@ test_expect_success 'adjacent, non-overlapping excluded regions' ' for_each_ref refs/heads/foo refs/heads/quux >expect && test_cmp expect actual && - assert_jumps 1 perf + case "$GIT_DEFAULT_REF_FORMAT" in + files) + assert_jumps 1 perf;; + reftable) + assert_jumps 2 perf;; + *) + BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";; + esac ' test_expect_success 'overlapping excluded regions' ' @@ -106,7 +114,30 @@ test_expect_success 'several overlapping excluded regions' ' for_each_ref refs/heads/quux >expect && test_cmp expect actual && - assert_jumps 1 perf + case "$GIT_DEFAULT_REF_FORMAT" in + files) + assert_jumps 1 perf;; + reftable) + assert_jumps 3 perf;; + *) + BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";; + esac +' + +test_expect_success 'unordered excludes' ' + for_each_ref__exclude refs/heads \ + refs/heads/foo refs/heads/baz >actual 2>perf && + for_each_ref refs/heads/bar refs/heads/quux >expect && + + test_cmp expect actual && + case "$GIT_DEFAULT_REF_FORMAT" in + files) + assert_jumps 1 perf;; + reftable) + assert_jumps 2 perf;; + *) + BUG "unhandled ref format $GIT_DEFAULT_REF_FORMAT";; + esac ' test_expect_success 'non-matching excluded section' ' diff --git a/t/t3011-common-prefixes-and-directory-traversal.sh b/t/t3011-common-prefixes-and-directory-traversal.sh index 3da5b2b6e7..69e44c387f 100755 --- a/t/t3011-common-prefixes-and-directory-traversal.sh +++ b/t/t3011-common-prefixes-and-directory-traversal.sh @@ -2,6 +2,7 @@ test_description='directory traversal handling, especially with common prefixes' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh index 94671d3c46..4d9744e5fc 100755 --- a/t/t3436-rebase-more-options.sh +++ b/t/t3436-rebase-more-options.sh @@ -5,6 +5,7 @@ test_description='tests to ensure compatibility between am and interactive backends' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 562aaf3a2a..b0ef0026e0 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -21,6 +21,8 @@ With -B, this should be detected as two complete rewrites. Further, with -B and -M together, these should turn into two renames. ' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh index d644310e22..1cea73ef5a 100755 --- a/t/t4017-diff-retval.sh +++ b/t/t4017-diff-retval.sh @@ -145,6 +145,14 @@ test_expect_success 'option errors are not confused by --exit-code' ' for option in --exit-code --quiet do + test_expect_success "git diff $option returns 1 for changed binary file" " + test_when_finished 'rm -f .gitattributes' && + git reset --hard && + echo a binary >.gitattributes && + echo 2 >>a && + test_expect_code 1 git diff $option + " + test_expect_success "git diff $option returns 1 for copied file" " git reset --hard && cp a copy && diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index 6fed993ea0..77bc36d5d8 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -2,6 +2,7 @@ test_description='rewrite diff' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff-data.sh diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 787605ce3f..e6f4fe441e 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -2,6 +2,7 @@ test_description='typechange rename detection' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh index c4394a27b5..1b8cd3e4c9 100755 --- a/t/t4031-diff-rewrite-binary.sh +++ b/t/t4031-diff-rewrite-binary.sh @@ -2,6 +2,7 @@ test_description='rewrite diff on binary file' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # We must be large enough to meet the MINIMUM_BREAK_SIZE diff --git a/t/t4056-diff-order.sh b/t/t4056-diff-order.sh index aec1d9d1b4..32c5fcb9a2 100755 --- a/t/t4056-diff-order.sh +++ b/t/t4056-diff-order.sh @@ -5,6 +5,7 @@ test_description='diff order & rotate' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh create_files () { diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh index ac72eeaf27..5e6e203aa5 100755 --- a/t/t4107-apply-ignore-whitespace.sh +++ b/t/t4107-apply-ignore-whitespace.sh @@ -3,9 +3,9 @@ # Copyright (c) 2009 Giuseppe Bilotta # -test_description='git-apply --ignore-whitespace. +test_description='git-apply --ignore-whitespace.' -' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # This primes main.c file that indents without using HT at all. diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 485c7d2d12..cdffee0273 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -2,6 +2,7 @@ test_description='core.whitespace rules and git apply' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh prepare_test_file () { diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh index 090987c89b..f248cc2a00 100755 --- a/t/t4125-apply-ws-fuzz.sh +++ b/t/t4125-apply-ws-fuzz.sh @@ -2,6 +2,7 @@ test_description='applying patch that has broken whitespaces in context' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh index 8bbf8260fa..7981931b4e 100755 --- a/t/t4138-apply-ws-expansion.sh +++ b/t/t4138-apply-ws-expansion.sh @@ -5,6 +5,7 @@ test_description='git apply test patches with whitespace expansion.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 605faea0c7..8e0f283c2b 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -5,6 +5,7 @@ test_description='git patch-id' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 7abba8a4b2..b9fda973f7 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -137,6 +137,8 @@ test_expect_success 'end-of-options is correctly eaten' ' test_expect_success 'populate workdir' ' mkdir a && + echo "a files_named_a" >.gitattributes && + git add .gitattributes && echo simple textfile >a/a && ten=0123456789 && hundred="$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten" && @@ -450,6 +452,16 @@ test_expect_success 'allow pathspecs that resolve to the current directory' ' test_cmp expect actual ' +test_expect_success 'attr pathspec in bare repo' ' + test_expect_code 0 git --git-dir=bare.git archive -v HEAD \ + ":(attr:files_named_a)" >/dev/null 2>actual && + cat >expect <<-\EOF && + a/ + a/a + EOF + test_cmp expect actual +' + # Pull the size and date of each entry in a tarfile using the system tar. # # We'll pull out only the year from the date; that avoids any question of diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index a6de7c5764..7044c7d7c6 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -2,6 +2,7 @@ test_description='exercise basic bitmap functionality' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-bitmap.sh diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index 832b92619c..6eaa692f33 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='exercise basic multi-pack bitmap functionality' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "${TEST_DIRECTORY}/lib-bitmap.sh" diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh index fc5fedbe9b..445739d06c 100755 --- a/t/t5329-pack-objects-cruft.sh +++ b/t/t5329-pack-objects-cruft.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='cruft pack related pack-objects tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh objdir=.git/objects diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh index 05090feaf9..f029ae0d28 100755 --- a/t/t5509-fetch-push-namespaces.sh +++ b/t/t5509-fetch-push-namespaces.sh @@ -96,6 +96,7 @@ test_expect_success 'hide namespaced refs with transfer.hideRefs' ' ' test_expect_success 'check that transfer.hideRefs does not match unstripped refs' ' + git -C pushee pack-refs --all && GIT_NAMESPACE=namespace \ git -C pushee -c transfer.hideRefs=refs/namespaces/namespace/refs/tags \ ls-remote "ext::git %s ." >actual && @@ -123,6 +124,14 @@ test_expect_success 'try to update a ref that is not hidden' ' git -C original push pushee-namespaced main ' +test_expect_success 'git-receive-pack(1) with transfer.hideRefs does not match unstripped refs during advertisement' ' + git -C pushee update-ref refs/namespaces/namespace/refs/heads/foo/1 refs/namespaces/namespace/refs/heads/main && + git -C pushee pack-refs --all && + test_config -C pushee transfer.hideRefs refs/namespaces/namespace/refs/heads/foo && + GIT_TRACE_PACKET="$(pwd)/trace" git -C original push pushee-namespaced main && + test_grep refs/heads/foo/1 trace +' + test_expect_success 'try to update a hidden full ref' ' test_config -C pushee transfer.hideRefs "^refs/namespaces/namespace/refs/heads/main" && test_must_fail git -C original push pushee-namespaced main diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index e36dfde17e..2ce88535a6 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -187,6 +187,28 @@ test_expect_success 'clone from password-protected repository' ' test_cmp expect actual ' +test_expect_success 'credential.interactive=false skips askpass' ' + set_askpass bogus nonsense && + ( + GIT_TRACE2_EVENT="$(pwd)/interactive-true" && + export GIT_TRACE2_EVENT && + test_must_fail git clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-true-dir && + test_region credential interactive interactive-true && + + GIT_TRACE2_EVENT="$(pwd)/interactive-false" && + export GIT_TRACE2_EVENT && + test_must_fail git -c credential.interactive=false \ + clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-false-dir && + test_region ! credential interactive interactive-false && + + GIT_TRACE2_EVENT="$(pwd)/interactive-never" && + export GIT_TRACE2_EVENT && + test_must_fail git -c credential.interactive=never \ + clone --bare "$HTTPD_URL/auth/smart/repo.git" interactive-never-dir && + test_region ! credential interactive interactive-never + ) +' + test_expect_success 'clone from auth-only-for-push repository' ' echo two >expect && set_askpass wrong && diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh index cd4f420e2a..5416241ede 100755 --- a/t/t6004-rev-list-path-optim.sh +++ b/t/t6004-rev-list-path-optim.sh @@ -16,6 +16,7 @@ test_description='git rev-list trivial path optimization test GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh index 738da23628..1aabab6956 100755 --- a/t/t6019-rev-list-ancestry-path.sh +++ b/t/t6019-rev-list-ancestry-path.sh @@ -29,6 +29,7 @@ test_description='--ancestry-path' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_merge () { diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh index 90ff141640..f63bc8d3da 100755 --- a/t/t6111-rev-list-treesame.sh +++ b/t/t6111-rev-list-treesame.sh @@ -16,6 +16,7 @@ test_description='TREESAME and limiting' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh note () { diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh index 2f9bea9793..64145a05b1 100755 --- a/t/t7061-wtstatus-ignore.sh +++ b/t/t7061-wtstatus-ignore.sh @@ -2,6 +2,7 @@ test_description='git-status ignored files' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh cat >expected <<\EOF diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 297c6c3b5c..0f0c86f9cb 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -12,6 +12,7 @@ submodule and "git submodule update --rebase/--merge" does not detach the HEAD. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 8d7b234beb..9f68893261 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -12,6 +12,7 @@ that are currently checked out. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh index d6040e0a33..7e1afa9ce4 100755 --- a/t/t7408-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh @@ -4,6 +4,8 @@ # test_description='test clone --reference' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh base_dir=$(pwd) diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh index 31271f8e0a..af0de496e0 100755 --- a/t/t7411-submodule-config.sh +++ b/t/t7411-submodule-config.sh @@ -10,6 +10,7 @@ from the database and from the worktree works. ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh index bf7f15ee79..d7fe910bbe 100755 --- a/t/t7420-submodule-set-url.sh +++ b/t/t7420-submodule-set-url.sh @@ -10,6 +10,7 @@ as expected. ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh index ab946ec940..c1686d6bb5 100755 --- a/t/t7422-submodule-output.sh +++ b/t/t7422-submodule-output.sh @@ -167,4 +167,11 @@ do ' done +test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE' ' + { git submodule status --recursive 2>err; echo $?>status; } | + grep -q X/S && + test_must_be_empty err && + test_match_signal 13 "$(cat status)" +' + test_done diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh index a88b02b06e..edce10f998 100755 --- a/t/t7521-ignored-mode.sh +++ b/t/t7521-ignored-mode.sh @@ -2,6 +2,7 @@ test_description='git status ignored modes' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup initial commit and ignore file' ' diff --git a/t/t7524-commit-summary.sh b/t/t7524-commit-summary.sh index 47b2f1dc22..a8fceb6a47 100755 --- a/t/t7524-commit-summary.sh +++ b/t/t7524-commit-summary.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='git commit summary' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh index a94387a75f..7fd8c086af 100755 --- a/t/t7601-merge-pull-config.sh +++ b/t/t7601-merge-pull-config.sh @@ -4,6 +4,7 @@ test_description='git merge Testing pull.* configuration parsing and other things.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index be1188e736..c4c3d1a15d 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -2,6 +2,7 @@ test_description='git repack works correctly' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "${TEST_DIRECTORY}/lib-bitmap.sh" . "${TEST_DIRECTORY}/lib-midx.sh" diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index cc917b257e..f67b9345b8 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -11,6 +11,7 @@ Testing basic diff tool invocation GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh difftool_test_setup () diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh index 167fe66150..55ed630e77 100755 --- a/t/t7814-grep-recurse-submodules.sh +++ b/t/t7814-grep-recurse-submodules.sh @@ -7,6 +7,7 @@ submodules. ' TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index abae7a9754..a66d0e089d 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -2,6 +2,7 @@ test_description='git maintenance builtin' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh GIT_TEST_COMMIT_GRAPH=0 @@ -825,6 +826,9 @@ test_expect_success 'start and stop Linux/systemd maintenance' ' test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" && test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" && + grep "core.askPass=true" "systemd/user/git-maintenance@.service" && + grep "credential.interactive=false" "systemd/user/git-maintenance@.service" && + printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect && test_cmp expect args && diff --git a/t/t9210-scalar.sh b/t/t9210-scalar.sh index e8613990e1..027235d61a 100755 --- a/t/t9210-scalar.sh +++ b/t/t9210-scalar.sh @@ -194,8 +194,11 @@ test_expect_success 'scalar reconfigure' ' scalar reconfigure one && test true = "$(git -C one/src config core.preloadIndex)" && git -C one/src config core.preloadIndex false && - scalar reconfigure -a && - test true = "$(git -C one/src config core.preloadIndex)" + rm one/src/cron.txt && + GIT_TRACE2_EVENT="$(pwd)/reconfigure" scalar reconfigure -a && + test_path_is_file one/src/cron.txt && + test true = "$(git -C one/src config core.preloadIndex)" && + test_subcommand git maintenance start <reconfigure ' test_expect_success 'scalar reconfigure --all with includeIf.onbranch' ' diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c new file mode 100644 index 0000000000..ab1fa44a28 --- /dev/null +++ b/t/unit-tests/lib-reftable.c @@ -0,0 +1,93 @@ +#include "lib-reftable.h" +#include "test-lib.h" +#include "reftable/constants.h" +#include "reftable/writer.h" + +void t_reftable_set_hash(uint8_t *p, int i, uint32_t id) +{ + memset(p, (uint8_t)i, hash_size(id)); +} + +static ssize_t strbuf_writer_write(void *b, const void *data, size_t sz) +{ + strbuf_add(b, data, sz); + return sz; +} + +static int strbuf_writer_flush(void *arg UNUSED) +{ + return 0; +} + +struct reftable_writer *t_reftable_strbuf_writer(struct strbuf *buf, + struct reftable_write_options *opts) +{ + return reftable_new_writer(&strbuf_writer_write, + &strbuf_writer_flush, + buf, opts); +} + +void t_reftable_write_to_buf(struct strbuf *buf, + struct reftable_ref_record *refs, + size_t nrefs, + struct reftable_log_record *logs, + size_t nlogs, + struct reftable_write_options *_opts) +{ + struct reftable_write_options opts = { 0 }; + const struct reftable_stats *stats; + struct reftable_writer *writer; + uint64_t min = 0xffffffff; + uint64_t max = 0; + int ret; + + if (_opts) + opts = *_opts; + + for (size_t i = 0; i < nrefs; i++) { + uint64_t ui = refs[i].update_index; + if (ui > max) + max = ui; + if (ui < min) + min = ui; + } + for (size_t i = 0; i < nlogs; i++) { + uint64_t ui = logs[i].update_index; + if (ui > max) + max = ui; + if (ui < min) + min = ui; + } + + writer = t_reftable_strbuf_writer(buf, &opts); + reftable_writer_set_limits(writer, min, max); + + if (nrefs) { + ret = reftable_writer_add_refs(writer, refs, nrefs); + check_int(ret, ==, 0); + } + + if (nlogs) { + ret = reftable_writer_add_logs(writer, logs, nlogs); + check_int(ret, ==, 0); + } + + ret = reftable_writer_close(writer); + check_int(ret, ==, 0); + + stats = reftable_writer_stats(writer); + for (size_t i = 0; i < stats->ref_stats.blocks; i++) { + size_t off = i * (opts.block_size ? opts.block_size + : DEFAULT_BLOCK_SIZE); + if (!off) + off = header_size(opts.hash_id == GIT_SHA256_FORMAT_ID ? 2 : 1); + check_char(buf->buf[off], ==, 'r'); + } + + if (nrefs) + check_int(stats->ref_stats.blocks, >, 0); + if (nlogs) + check_int(stats->log_stats.blocks, >, 0); + + reftable_writer_free(writer); +} diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h new file mode 100644 index 0000000000..d115419084 --- /dev/null +++ b/t/unit-tests/lib-reftable.h @@ -0,0 +1,20 @@ +#ifndef LIB_REFTABLE_H +#define LIB_REFTABLE_H + +#include "git-compat-util.h" +#include "strbuf.h" +#include "reftable/reftable-writer.h" + +void t_reftable_set_hash(uint8_t *p, int i, uint32_t id); + +struct reftable_writer *t_reftable_strbuf_writer(struct strbuf *buf, + struct reftable_write_options *opts); + +void t_reftable_write_to_buf(struct strbuf *buf, + struct reftable_ref_record *refs, + size_t nrecords, + struct reftable_log_record *logs, + size_t nlogs, + struct reftable_write_options *opts); + +#endif diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c index e9d100a01e..19e54bdfb8 100644 --- a/t/unit-tests/t-reftable-merged.c +++ b/t/unit-tests/t-reftable-merged.c @@ -7,6 +7,7 @@ https://developers.google.com/open-source/licenses/bsd */ #include "test-lib.h" +#include "lib-reftable.h" #include "reftable/blocksource.h" #include "reftable/constants.h" #include "reftable/merged.h" @@ -15,77 +16,6 @@ https://developers.google.com/open-source/licenses/bsd #include "reftable/reftable-merged.h" #include "reftable/reftable-writer.h" -static ssize_t strbuf_add_void(void *b, const void *data, const size_t sz) -{ - strbuf_add(b, data, sz); - return sz; -} - -static int noop_flush(void *arg UNUSED) -{ - return 0; -} - -static void write_test_table(struct strbuf *buf, - struct reftable_ref_record refs[], const size_t n) -{ - uint64_t min = 0xffffffff; - uint64_t max = 0; - size_t i; - int err; - - struct reftable_write_options opts = { - .block_size = 256, - }; - struct reftable_writer *w = NULL; - for (i = 0; i < n; i++) { - uint64_t ui = refs[i].update_index; - if (ui > max) - max = ui; - if (ui < min) - min = ui; - } - - w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); - reftable_writer_set_limits(w, min, max); - - for (i = 0; i < n; i++) { - uint64_t before = refs[i].update_index; - int n = reftable_writer_add_ref(w, &refs[i]); - check_int(n, ==, 0); - check_int(before, ==, refs[i].update_index); - } - - err = reftable_writer_close(w); - check(!err); - - reftable_writer_free(w); -} - -static void write_test_log_table(struct strbuf *buf, struct reftable_log_record logs[], - const size_t n, const uint64_t update_index) -{ - int err; - - struct reftable_write_options opts = { - .block_size = 256, - .exact_log_message = 1, - }; - struct reftable_writer *w = NULL; - w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); - reftable_writer_set_limits(w, update_index, update_index); - - for (size_t i = 0; i < n; i++) { - int err = reftable_writer_add_log(w, &logs[i]); - check(!err); - } - - err = reftable_writer_close(w); - check(!err); - - reftable_writer_free(w); -} - static struct reftable_merged_table * merged_table_from_records(struct reftable_ref_record **refs, struct reftable_block_source **source, @@ -93,13 +23,16 @@ merged_table_from_records(struct reftable_ref_record **refs, struct strbuf *buf, const size_t n) { struct reftable_merged_table *mt = NULL; + struct reftable_write_options opts = { + .block_size = 256, + }; int err; REFTABLE_CALLOC_ARRAY(*readers, n); REFTABLE_CALLOC_ARRAY(*source, n); for (size_t i = 0; i < n; i++) { - write_test_table(&buf[i], refs[i], sizes[i]); + t_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts); block_source_from_strbuf(&(*source)[i], &buf[i]); err = reftable_reader_new(&(*readers)[i], &(*source)[i], @@ -261,6 +194,81 @@ static void t_merged_refs(void) reftable_free(bs); } +static void t_merged_seek_multiple_times(void) +{ + struct reftable_ref_record r1[] = { + { + .refname = (char *) "a", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1 }, + }, + { + .refname = (char *) "c", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 2 }, + } + }; + struct reftable_ref_record r2[] = { + { + .refname = (char *) "b", + .update_index = 2, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 3 }, + }, + { + .refname = (char *) "d", + .update_index = 2, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 4 }, + }, + }; + struct reftable_ref_record *refs[] = { + r1, r2, + }; + size_t sizes[] = { + ARRAY_SIZE(r1), ARRAY_SIZE(r2), + }; + struct strbuf bufs[] = { + STRBUF_INIT, STRBUF_INIT, + }; + struct reftable_block_source *sources = NULL; + struct reftable_reader **readers = NULL; + struct reftable_ref_record rec = { 0 }; + struct reftable_iterator it = { 0 }; + struct reftable_merged_table *mt; + + mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2); + merged_table_init_iter(mt, &it, BLOCK_TYPE_REF); + + for (size_t i = 0; i < 5; i++) { + int err = reftable_iterator_seek_ref(&it, "c"); + check(!err); + + err = reftable_iterator_next_ref(&it, &rec); + check(!err); + err = reftable_ref_record_equal(&rec, &r1[1], GIT_SHA1_RAWSZ); + check(err == 1); + + err = reftable_iterator_next_ref(&it, &rec); + check(!err); + err = reftable_ref_record_equal(&rec, &r2[1], GIT_SHA1_RAWSZ); + check(err == 1); + + err = reftable_iterator_next_ref(&it, &rec); + check(err > 0); + } + + for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) + strbuf_release(&bufs[i]); + readers_destroy(readers, ARRAY_SIZE(refs)); + reftable_ref_record_release(&rec); + reftable_iterator_destroy(&it); + reftable_merged_table_free(mt); + reftable_free(sources); +} + static struct reftable_merged_table * merged_table_from_log_records(struct reftable_log_record **logs, struct reftable_block_source **source, @@ -268,13 +276,17 @@ merged_table_from_log_records(struct reftable_log_record **logs, struct strbuf *buf, const size_t n) { struct reftable_merged_table *mt = NULL; + struct reftable_write_options opts = { + .block_size = 256, + .exact_log_message = 1, + }; int err; REFTABLE_CALLOC_ARRAY(*readers, n); REFTABLE_CALLOC_ARRAY(*source, n); for (size_t i = 0; i < n; i++) { - write_test_log_table(&buf[i], logs[i], sizes[i], i + 1); + t_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts); block_source_from_strbuf(&(*source)[i], &buf[i]); err = reftable_reader_new(&(*readers)[i], &(*source)[i], @@ -402,9 +414,7 @@ static void t_default_write_opts(void) { struct reftable_write_options opts = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); - + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record rec = { .refname = (char *) "master", .update_index = 1, @@ -448,6 +458,7 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED) TEST(t_default_write_opts(), "merged table with default write opts"); TEST(t_merged_logs(), "merged table with multiple log updates for same ref"); TEST(t_merged_refs(), "merged table with multiple updates to same ref"); + TEST(t_merged_seek_multiple_times(), "merged table can seek multiple times"); TEST(t_merged_single_record(), "ref ocurring in only one record can be fetched"); return test_done(); diff --git a/t/unit-tests/t-reftable-reader.c b/t/unit-tests/t-reftable-reader.c new file mode 100644 index 0000000000..eea86966c0 --- /dev/null +++ b/t/unit-tests/t-reftable-reader.c @@ -0,0 +1,96 @@ +#include "test-lib.h" +#include "lib-reftable.h" +#include "reftable/blocksource.h" +#include "reftable/reader.h" + +static int t_reader_seek_once(void) +{ + struct reftable_ref_record records[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_ref_record ref = { 0 }; + struct reftable_iterator it = { 0 }; + struct reftable_reader *reader; + struct strbuf buf = STRBUF_INIT; + int ret; + + t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); + block_source_from_strbuf(&source, &buf); + + ret = reftable_reader_new(&reader, &source, "name"); + check(!ret); + + reftable_reader_init_ref_iterator(reader, &it); + ret = reftable_iterator_seek_ref(&it, ""); + check(!ret); + ret = reftable_iterator_next_ref(&it, &ref); + check(!ret); + + ret = reftable_ref_record_equal(&ref, &records[0], GIT_SHA1_RAWSZ); + check_int(ret, ==, 1); + + ret = reftable_iterator_next_ref(&it, &ref); + check_int(ret, ==, 1); + + reftable_ref_record_release(&ref); + reftable_iterator_destroy(&it); + reftable_reader_decref(reader); + strbuf_release(&buf); + return 0; +} + +static int t_reader_reseek(void) +{ + struct reftable_ref_record records[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_ref_record ref = { 0 }; + struct reftable_iterator it = { 0 }; + struct reftable_reader *reader; + struct strbuf buf = STRBUF_INIT; + int ret; + + t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); + block_source_from_strbuf(&source, &buf); + + ret = reftable_reader_new(&reader, &source, "name"); + check(!ret); + + reftable_reader_init_ref_iterator(reader, &it); + + for (size_t i = 0; i < 5; i++) { + ret = reftable_iterator_seek_ref(&it, ""); + check(!ret); + ret = reftable_iterator_next_ref(&it, &ref); + check(!ret); + + ret = reftable_ref_record_equal(&ref, &records[0], GIT_SHA1_RAWSZ); + check_int(ret, ==, 1); + + ret = reftable_iterator_next_ref(&it, &ref); + check_int(ret, ==, 1); + } + + reftable_ref_record_release(&ref); + reftable_iterator_destroy(&it); + reftable_reader_decref(reader); + strbuf_release(&buf); + return 0; +} + +int cmd_main(int argc UNUSED, const char *argv[] UNUSED) +{ + TEST(t_reader_seek_once(), "reader can seek once"); + TEST(t_reader_reseek(), "reader can reseek multiple times"); + return test_done(); +} diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c index 82bfaf3287..e1b235a5f1 100644 --- a/t/unit-tests/t-reftable-readwrite.c +++ b/t/unit-tests/t-reftable-readwrite.c @@ -7,6 +7,7 @@ https://developers.google.com/open-source/licenses/bsd */ #include "test-lib.h" +#include "lib-reftable.h" #include "reftable/basics.h" #include "reftable/blocksource.h" #include "reftable/reader.h" @@ -15,22 +16,6 @@ https://developers.google.com/open-source/licenses/bsd static const int update_index = 5; -static void set_test_hash(uint8_t *p, int i) -{ - memset(p, (uint8_t)i, hash_size(GIT_SHA1_FORMAT_ID)); -} - -static ssize_t strbuf_add_void(void *b, const void *data, size_t sz) -{ - strbuf_add(b, data, sz); - return sz; -} - -static int noop_flush(void *arg UNUSED) -{ - return 0; -} - static void t_buffer(void) { struct strbuf buf = STRBUF_INIT; @@ -62,61 +47,34 @@ static void write_table(char ***names, struct strbuf *buf, int N, .block_size = block_size, .hash_id = hash_id, }; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); - struct reftable_ref_record ref = { 0 }; - int i = 0, n; - struct reftable_log_record log = { 0 }; - const struct reftable_stats *stats = NULL; + struct reftable_ref_record *refs; + struct reftable_log_record *logs; + int i; REFTABLE_CALLOC_ARRAY(*names, N + 1); + REFTABLE_CALLOC_ARRAY(refs, N); + REFTABLE_CALLOC_ARRAY(logs, N); - reftable_writer_set_limits(w, update_index, update_index); for (i = 0; i < N; i++) { - char name[100]; - int n; - - snprintf(name, sizeof(name), "refs/heads/branch%02d", i); - - ref.refname = name; - ref.update_index = update_index; - ref.value_type = REFTABLE_REF_VAL1; - set_test_hash(ref.value.val1, i); - (*names)[i] = xstrdup(name); - - n = reftable_writer_add_ref(w, &ref); - check_int(n, ==, 0); + refs[i].refname = (*names)[i] = xstrfmt("refs/heads/branch%02d", i); + refs[i].update_index = update_index; + refs[i].value_type = REFTABLE_REF_VAL1; + t_reftable_set_hash(refs[i].value.val1, i, GIT_SHA1_FORMAT_ID); } for (i = 0; i < N; i++) { - char name[100]; - int n; - - snprintf(name, sizeof(name), "refs/heads/branch%02d", i); - - log.refname = name; - log.update_index = update_index; - log.value_type = REFTABLE_LOG_UPDATE; - set_test_hash(log.value.update.new_hash, i); - log.value.update.message = (char *) "message"; - - n = reftable_writer_add_log(w, &log); - check_int(n, ==, 0); + logs[i].refname = (*names)[i]; + logs[i].update_index = update_index; + logs[i].value_type = REFTABLE_LOG_UPDATE; + t_reftable_set_hash(logs[i].value.update.new_hash, i, + GIT_SHA1_FORMAT_ID); + logs[i].value.update.message = (char *) "message"; } - n = reftable_writer_close(w); - check_int(n, ==, 0); - - stats = reftable_writer_stats(w); - for (i = 0; i < stats->ref_stats.blocks; i++) { - int off = i * opts.block_size; - if (!off) - off = header_size((hash_id == GIT_SHA256_FORMAT_ID) ? 2 : 1); - check_char(buf->buf[off], ==, 'r'); - } + t_reftable_write_to_buf(buf, refs, N, logs, N, &opts); - check_int(stats->log_stats.blocks, >, 0); - reftable_writer_free(w); + free(refs); + free(logs); } static void t_log_buffer_size(void) @@ -138,8 +96,7 @@ static void t_log_buffer_size(void) .time = 0x5e430672, .message = (char *) "commit: 9\n", } } }; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); /* This tests buffer extension for log compression. Must use a random hash, to ensure that the compressed part is larger than the original. @@ -181,8 +138,7 @@ static void t_log_overflow(void) }, }, }; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); memset(msg, 'x', sizeof(msg) - 1); reftable_writer_set_limits(w, update_index, update_index); @@ -208,8 +164,7 @@ static void t_log_write_read(void) struct reftable_reader *reader; struct reftable_block_source source = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); const struct reftable_stats *stats = NULL; reftable_writer_set_limits(w, 0, N); for (i = 0; i < N; i++) { @@ -229,8 +184,10 @@ static void t_log_write_read(void) log.refname = names[i]; log.update_index = i; log.value_type = REFTABLE_LOG_UPDATE; - set_test_hash(log.value.update.old_hash, i); - set_test_hash(log.value.update.new_hash, i + 1); + t_reftable_set_hash(log.value.update.old_hash, i, + GIT_SHA1_FORMAT_ID); + t_reftable_set_hash(log.value.update.new_hash, i + 1, + GIT_SHA1_FORMAT_ID); err = reftable_writer_add_log(w, &log); check(!err); @@ -297,8 +254,7 @@ static void t_log_zlib_corruption(void) struct reftable_reader *reader; struct reftable_block_source source = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); const struct reftable_stats *stats = NULL; char message[100] = { 0 }; int err, i, n; @@ -528,15 +484,12 @@ static void t_table_refs_for(int indexed) int err; struct reftable_reader *reader; struct reftable_block_source source = { 0 }; - struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); - + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_iterator it = { 0 }; int j; - set_test_hash(want_hash, 4); + t_reftable_set_hash(want_hash, 4, GIT_SHA1_FORMAT_ID); for (i = 0; i < N; i++) { uint8_t hash[GIT_SHA1_RAWSZ]; @@ -552,8 +505,10 @@ static void t_table_refs_for(int indexed) ref.refname = name; ref.value_type = REFTABLE_REF_VAL2; - set_test_hash(ref.value.val2.value, i / 4); - set_test_hash(ref.value.val2.target_value, 3 + i / 4); + t_reftable_set_hash(ref.value.val2.value, i / 4, + GIT_SHA1_FORMAT_ID); + t_reftable_set_hash(ref.value.val2.target_value, 3 + i / 4, + GIT_SHA1_FORMAT_ID); /* 80 bytes / entry, so 3 entries per block. Yields 17 */ @@ -618,8 +573,7 @@ static void t_write_empty_table(void) { struct reftable_write_options opts = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_block_source source = { 0 }; struct reftable_reader *rd = NULL; struct reftable_ref_record rec = { 0 }; @@ -657,8 +611,7 @@ static void t_write_object_id_min_length(void) .block_size = 75, }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, @@ -692,8 +645,7 @@ static void t_write_object_id_length(void) .block_size = 75, }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, @@ -726,8 +678,7 @@ static void t_write_empty_key(void) { struct reftable_write_options opts = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .refname = (char *) "", .update_index = 1, @@ -749,8 +700,7 @@ static void t_write_key_order(void) { struct reftable_write_options opts = { 0 }; struct strbuf buf = STRBUF_INIT; - struct reftable_writer *w = - reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record refs[2] = { { .refname = (char *) "b", @@ -798,7 +748,7 @@ static void t_write_multiple_indices(void) struct reftable_reader *reader; int err, i; - writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts); + writer = t_reftable_strbuf_writer(&writer_buf, &opts); reftable_writer_set_limits(writer, 1, 1); for (i = 0; i < 100; i++) { struct reftable_ref_record ref = { @@ -876,7 +826,7 @@ static void t_write_multi_level_index(void) struct reftable_reader *reader; int err; - writer = reftable_new_writer(&strbuf_add_void, &noop_flush, &writer_buf, &opts); + writer = t_reftable_strbuf_writer(&writer_buf, &opts); reftable_writer_set_limits(writer, 1, 1); for (size_t i = 0; i < 200; i++) { struct reftable_ref_record ref = { diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c index d62a9c1bed..31d563d992 100644 --- a/t/unit-tests/t-reftable-stack.c +++ b/t/unit-tests/t-reftable-stack.c @@ -7,17 +7,13 @@ https://developers.google.com/open-source/licenses/bsd */ #include "test-lib.h" +#include "lib-reftable.h" #include "reftable/merged.h" #include "reftable/reader.h" #include "reftable/reftable-error.h" #include "reftable/stack.h" #include <dirent.h> -static void set_test_hash(uint8_t *p, int i) -{ - memset(p, (uint8_t)i, hash_size(GIT_SHA1_FORMAT_ID)); -} - static void clear_dir(const char *dirname) { struct strbuf path = STRBUF_INIT; @@ -125,7 +121,7 @@ static void write_n_ref_tables(struct reftable_stack *st, strbuf_reset(&buf); strbuf_addf(&buf, "refs/heads/branch-%04"PRIuMAX, (uintmax_t)i); ref.refname = buf.buf; - set_test_hash(ref.value.val1, i); + t_reftable_set_hash(ref.value.val1, i, GIT_SHA1_FORMAT_ID); err = reftable_stack_add(st, &write_test_ref, &ref); check(!err); @@ -271,7 +267,7 @@ static void t_reftable_stack_transaction_api(void) reftable_addition_destroy(add); - err = reftable_stack_new_addition(&add, st); + err = reftable_stack_new_addition(&add, st, 0); check(!err); err = reftable_addition_add(add, write_test_ref, &ref); @@ -292,6 +288,68 @@ static void t_reftable_stack_transaction_api(void) clear_dir(dir); } +static void t_reftable_stack_transaction_with_reload(void) +{ + char *dir = get_tmp_dir(__LINE__); + struct reftable_stack *st1 = NULL, *st2 = NULL; + int err; + struct reftable_addition *add = NULL; + struct reftable_ref_record refs[2] = { + { + .refname = (char *) "refs/heads/a", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { '1' }, + }, + { + .refname = (char *) "refs/heads/b", + .update_index = 2, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { '1' }, + }, + }; + struct reftable_ref_record ref = { 0 }; + + err = reftable_new_stack(&st1, dir, NULL); + check(!err); + err = reftable_new_stack(&st2, dir, NULL); + check(!err); + + err = reftable_stack_new_addition(&add, st1, 0); + check(!err); + err = reftable_addition_add(add, write_test_ref, &refs[0]); + check(!err); + err = reftable_addition_commit(add); + check(!err); + reftable_addition_destroy(add); + + /* + * The second stack is now outdated, which we should notice. We do not + * create the addition and lock the stack by default, but allow the + * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set. + */ + err = reftable_stack_new_addition(&add, st2, 0); + check_int(err, ==, REFTABLE_OUTDATED_ERROR); + err = reftable_stack_new_addition(&add, st2, REFTABLE_STACK_NEW_ADDITION_RELOAD); + check(!err); + err = reftable_addition_add(add, write_test_ref, &refs[1]); + check(!err); + err = reftable_addition_commit(add); + check(!err); + reftable_addition_destroy(add); + + for (size_t i = 0; i < ARRAY_SIZE(refs); i++) { + err = reftable_stack_read_ref(st2, refs[i].refname, &ref); + check(!err); + check(reftable_ref_record_equal(&refs[i], &ref, GIT_SHA1_RAWSZ)); + } + + reftable_ref_record_release(&ref); + reftable_stack_destroy(st1); + reftable_stack_destroy(st2); + clear_dir(dir); +} + static void t_reftable_stack_transaction_api_performs_auto_compaction(void) { char *dir = get_tmp_dir(__LINE__); @@ -322,7 +380,7 @@ static void t_reftable_stack_transaction_api_performs_auto_compaction(void) */ st->opts.disable_auto_compact = i != n; - err = reftable_stack_new_addition(&add, st); + err = reftable_stack_new_addition(&add, st, 0); check(!err); err = reftable_addition_add(add, write_test_ref, &ref); @@ -470,13 +528,13 @@ static void t_reftable_stack_add(void) refs[i].refname = xstrdup(buf); refs[i].update_index = i + 1; refs[i].value_type = REFTABLE_REF_VAL1; - set_test_hash(refs[i].value.val1, i); + t_reftable_set_hash(refs[i].value.val1, i, GIT_SHA1_FORMAT_ID); logs[i].refname = xstrdup(buf); logs[i].update_index = N + i + 1; logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.email = xstrdup("identity@invalid"); - set_test_hash(logs[i].value.update.new_hash, i); + t_reftable_set_hash(logs[i].value.update.new_hash, i, GIT_SHA1_FORMAT_ID); } for (i = 0; i < N; i++) { @@ -562,14 +620,14 @@ static void t_reftable_stack_iterator(void) refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); refs[i].update_index = i + 1; refs[i].value_type = REFTABLE_REF_VAL1; - set_test_hash(refs[i].value.val1, i); + t_reftable_set_hash(refs[i].value.val1, i, GIT_SHA1_FORMAT_ID); logs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); logs[i].update_index = i + 1; logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.email = xstrdup("johndoe@invalid"); logs[i].value.update.message = xstrdup("commit\n"); - set_test_hash(logs[i].value.update.new_hash, i); + t_reftable_set_hash(logs[i].value.update.new_hash, i, GIT_SHA1_FORMAT_ID); } for (i = 0; i < N; i++) { @@ -704,7 +762,8 @@ static void t_reftable_stack_tombstone(void) refs[i].update_index = i + 1; if (i % 2 == 0) { refs[i].value_type = REFTABLE_REF_VAL1; - set_test_hash(refs[i].value.val1, i); + t_reftable_set_hash(refs[i].value.val1, i, + GIT_SHA1_FORMAT_ID); } logs[i].refname = xstrdup(buf); @@ -712,7 +771,8 @@ static void t_reftable_stack_tombstone(void) logs[i].update_index = 42; if (i % 2 == 0) { logs[i].value_type = REFTABLE_LOG_UPDATE; - set_test_hash(logs[i].value.update.new_hash, i); + t_reftable_set_hash(logs[i].value.update.new_hash, i, + GIT_SHA1_FORMAT_ID); logs[i].value.update.email = xstrdup("identity@invalid"); } @@ -844,7 +904,8 @@ static void t_reflog_expire(void) logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.time = i; logs[i].value.update.email = xstrdup("identity@invalid"); - set_test_hash(logs[i].value.update.new_hash, i); + t_reftable_set_hash(logs[i].value.update.new_hash, i, + GIT_SHA1_FORMAT_ID); } for (i = 1; i <= N; i++) { @@ -1314,6 +1375,7 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED) TEST(t_reftable_stack_reload_with_missing_table(), "stack iteration with garbage tables"); TEST(t_reftable_stack_tombstone(), "'tombstone' refs in stack"); TEST(t_reftable_stack_transaction_api(), "update transaction to stack"); + TEST(t_reftable_stack_transaction_with_reload(), "transaction with reload"); TEST(t_reftable_stack_transaction_api_performs_auto_compaction(), "update transaction triggers auto-compaction"); TEST(t_reftable_stack_update_index_check(), "update transactions with equal update indices"); TEST(t_reftable_stack_uptodate(), "stack must be reloaded before ref update"); @@ -554,6 +554,7 @@ enum trace2_counter_id { TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */ TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */ + TRACE2_COUNTER_ID_REFTABLE_RESEEKS, /* counts number of re-seeks */ /* counts number of fsyncs */ TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY, diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c index d3a33715c1..036b643578 100644 --- a/trace2/tr2_ctr.c +++ b/trace2/tr2_ctr.c @@ -31,6 +31,11 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER .name = "jumps_made", .want_per_thread_events = 0, }, + [TRACE2_COUNTER_ID_REFTABLE_RESEEKS] = { + .category = "reftable", + .name = "reseeks_made", + .want_per_thread_events = 0, + }, [TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY] = { .category = "fsync", .name = "writeout-only", diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c index 4f75392952..7b023c1bfc 100644 --- a/trace2/tr2_tls.c +++ b/trace2/tr2_tls.c @@ -152,11 +152,19 @@ uint64_t tr2tls_absolute_elapsed(uint64_t us) return us - tr2tls_us_start_process; } +static void tr2tls_key_destructor(void *payload) +{ + struct tr2tls_thread_ctx *ctx = payload; + free((char *)ctx->thread_name); + free(ctx->array_us_start); + free(ctx); +} + void tr2tls_init(void) { tr2tls_start_process_clock(); - pthread_key_create(&tr2tls_key, NULL); + pthread_key_create(&tr2tls_key, tr2tls_key_destructor); init_recursive_mutex(&tr2tls_mutex); tr2tls_thread_main = diff --git a/upload-pack.c b/upload-pack.c index c84c3c3b1f..6d6e0f9f98 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -1812,7 +1812,7 @@ int upload_pack_v2(struct repository *r, struct packet_reader *request) } else { /* * Request had 'want's but no 'have's so we can - * immedietly go to construct and send a pack. + * immediately go to construct and send a pack. */ state = FETCH_SEND_PACK; } |
