diff options
54 files changed, 773 insertions, 455 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 079645b776..1b41278a7f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,6 +5,19 @@ on: [push, pull_request] env: DEVELOPER: 1 +# If more than one workflow run is triggered for the very same commit hash +# (which happens when multiple branches pointing to the same commit), only +# the first one is allowed to run, the second will be kept in the "queued" +# state. This allows a successful completion of the first run to be reused +# in the second run via the `skip-if-redundant` logic in the `config` job. +# +# The only caveat is that if a workflow run is triggered for the same commit +# hash that another run is already being held, that latter run will be +# canceled. For more details about the `concurrency` attribute, see: +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency +concurrency: + group: ${{ github.sha }} + jobs: ci-config: name: config diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt index 6cad659605..91d7bc9b24 100644 --- a/Documentation/RelNotes/2.43.0.txt +++ b/Documentation/RelNotes/2.43.0.txt @@ -22,10 +22,34 @@ UI, Workflows & Features * Update two credential helpers to correctly match which credential to erase; they dropped not the ones with stale password. + * Git GUI updates. + + * "git format-patch" learns a way to feed cover letter description, + that (1) can be used on detached HEAD where there is no branch + description available, and (2) also can override the branch + description if there is one. + Performance, Internal Implementation, Development Support etc. - * TBD. + * "git check-attr" has been taught to work better with sparse-index. + + * It may be tempting to leave the help text NULL for a command line + option that is either hidden or too obvious, but "git subcmd -h" + and "git subcmd --help-all" would have segfaulted if done so. Now + the help text is optional. + + * Tests that are known to pass with LSan are now marked as such. + (merge 5fafe8c95f tb/mark-more-tests-as-leak-free later to maint). + + * Flakey "git p4" tests, as well as "git svn" tests, are now skipped + in the (rather expensive) sanitizer CI job. + (merge 6ba913629f js/ci-san-skip-p4-and-svn-tests later to maint). + + * Tests with LSan from time to time seem to emit harmless message + that makes our tests unnecessarily flakey; we work it around by + filtering the uninteresting output. + (merge 370ef7e40d jk/test-lsan-denoise-output later to maint). Fixes since v2.42 @@ -35,9 +59,39 @@ Fixes since v2.42 chopped to fit under filesystem limitation. (merge ac300bda10 mp/rebase-label-length-limit later to maint). + * Scalar updates. + (merge f9a547d3a7 ds/scalar-updates later to maint). + + * Tweak GitHub Actions CI so that pushing the same commit to multiple + branch tips at the same time will not waste building and testing + the same thing twice. + (merge 99fe06cbfd jc/ci-skip-same-commit later to maint). + + * The commit-graph verification code that detects mixture of zero and + non-zero generation numbers has been updated. + (merge db6044d762 tb/commit-graph-verify-fix later to maint). + + * "git diff -w --exit-code" with various options did not work + correctly, which is being addressed. + (merge a64f8b2595 jc/diff-exit-code-with-w-fixes later to maint). + + * transfer.unpackLimit ought to be used as a fallback, but overrode + fetch.unpackLimit and receive.unpackLimit instead. + (merge f3d33f8cfe ts/unpacklimit-config-fix later to maint). + + * The use of API between two calls to require_clean_work_tree() from + the sequencer code has been cleaned up for consistency. + (merge a9b5955e07 ob/sequencer-empty-hint-fix later to maint). + + * "git diff --no-such-option" and other corner cases around the exit + status of the "diff" command has been corrected. + (merge 5cc6b2d70b jk/diff-result-code-cleanup later to maint). + * Other code cleanup, docfix, build fix, etc. (merge fd3ba590d8 ws/git-push-doc-grammofix later to maint). (merge 5f33a843de ds/upload-pack-error-sequence-fix later to maint). (merge beaa1d952b jk/function-pointer-mismatches-fix later to maint). (merge b46d806ea5 ob/t9001-indent-fix later to maint). (merge fdc9914c28 ja/worktree-orphan later to maint). + (merge c2cbefc510 jc/mv-d-to-d-error-message-fix later to maint). + (merge d0fc552bfc ch/t6300-verify-commit-test-cleanup later to maint). diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 373b46fc0d..8e515c7dbb 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -215,6 +215,10 @@ is greater than 100 bytes, then the mode will be `message`, otherwise If `<mode>` is `none`, both the cover letter subject and body will be populated with placeholder text. +--description-file=<file>:: + Use the contents of <file> instead of the branch's description + for generating the cover letter. + --subject-prefix=<subject prefix>:: Instead of the standard '[PATCH]' prefix in the subject line, instead use '[<subject prefix>]'. This diff --git a/Documentation/scalar.txt b/Documentation/scalar.txt index f33436c7f6..361f51a647 100644 --- a/Documentation/scalar.txt +++ b/Documentation/scalar.txt @@ -8,7 +8,8 @@ scalar - A tool for managing large Git repositories SYNOPSIS -------- [verse] -scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>] +scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] + [--[no-]src] <url> [<enlistment>] scalar list scalar register [<enlistment>] scalar unregister [<enlistment>] @@ -80,6 +81,11 @@ remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when `--single-branch` clone was made, no remote-tracking branch is created. +--[no-]src:: + By default, `scalar clone` places the cloned repository within a + `<entlistment>/src` directory. Use `--no-src` to place the cloned + repository directly in the `<enlistment>` directory. + --[no-]full-clone:: A sparse-checkout is initialized by default. This behavior can be turned off via `--full-clone`. diff --git a/add-interactive.c b/add-interactive.c index add9a1ad43..7fd00c5e25 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -569,7 +569,7 @@ static int get_modified_files(struct repository *r, copy_pathspec(&rev.prune_data, ps); if (s.mode == FROM_INDEX) - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); else { rev.diffopt.flags.ignore_dirty_submodules = 1; run_diff_files(&rev, 0); @@ -807,35 +807,56 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate, static struct attr_stack *read_attr_from_index(struct index_state *istate, const char *path, unsigned flags) { + struct attr_stack *stack = NULL; char *buf; unsigned long size; + int sparse_dir_pos = -1; if (!istate) return NULL; /* - * The .gitattributes file only applies to files within its - * parent directory. In the case of cone-mode sparse-checkout, - * the .gitattributes file is sparse if and only if all paths - * within that directory are also sparse. Thus, don't load the - * .gitattributes file since it will not matter. - * - * In the case of a sparse index, it is critical that we don't go - * looking for a .gitattributes file, as doing so would cause the - * index to expand. + * When handling sparse-checkouts, .gitattributes files + * may reside within a sparse directory. We distinguish + * whether a path exists directly in the index or not by + * evaluating if 'pos' is negative. + * If 'pos' is negative, the path is not directly present + * in the index and is likely within a sparse directory. + * For paths not in the index, The absolute value of 'pos' + * minus 1 gives us the position where the path would be + * inserted in lexicographic order within the index. + * We then subtract another 1 from this value + * (sparse_dir_pos = -pos - 2) to find the position of the + * last index entry which is lexicographically smaller than + * the path. This would be the sparse directory containing + * the path. By identifying the sparse directory containing + * the path, we can correctly read the attributes specified + * in the .gitattributes file from the tree object of the + * sparse directory. */ - if (!path_in_cone_mode_sparse_checkout(path, istate)) - return NULL; + if (!path_in_cone_mode_sparse_checkout(path, istate)) { + int pos = index_name_pos_sparse(istate, path, strlen(path)); - buf = read_blob_data_from_index(istate, path, &size); - if (!buf) - return NULL; - if (size >= ATTR_MAX_FILE_SIZE) { - warning(_("ignoring overly large gitattributes blob '%s'"), path); - return NULL; + if (pos < 0) + sparse_dir_pos = -pos - 2; } - return read_attr_from_buf(buf, path, flags); + if (sparse_dir_pos >= 0 && + S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) && + !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) { + const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]); + stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags); + } else { + buf = read_blob_data_from_index(istate, path, &size); + if (!buf) + return NULL; + if (size >= ATTR_MAX_FILE_SIZE) { + warning(_("ignoring overly large gitattributes blob '%s'"), path); + return NULL; + } + stack = read_attr_from_buf(buf, path, flags); + } + return stack; } static struct attr_stack *read_attr(struct index_state *istate, diff --git a/builtin/add.c b/builtin/add.c index 4b0dd798df..12c5aa6d1f 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -194,8 +194,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); rev.diffopt.file = xfdopen(out, "w"); rev.diffopt.close_file = 1; - if (run_diff_files(&rev, 0)) - die(_("Could not write patch")); + run_diff_files(&rev, 0); if (launch_editor(file, NULL, NULL)) die(_("editing patch failed")); diff --git a/builtin/am.c b/builtin/am.c index 8bde034fae..202040b62e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1430,7 +1430,7 @@ static void write_index_patch(const struct am_state *state) rev_info.diffopt.close_file = 1; add_pending_object(&rev_info, &tree->object, ""); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } @@ -1593,7 +1593,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa rev_info.diffopt.filter |= diff_filter_bit('M'); add_pending_oid(&rev_info, "HEAD", &our_tree, 0); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } diff --git a/builtin/check-attr.c b/builtin/check-attr.c index b22ff748c3..c1da1d184e 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -122,6 +122,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } diff --git a/builtin/describe.c b/builtin/describe.c index b28a4a1f82..a9e375882b 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -668,7 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) struct lock_file index_lock = LOCK_INIT; struct rev_info revs; struct strvec args = STRVEC_INIT; - int fd, result; + int fd; setup_work_tree(); prepare_repo_settings(the_repository); @@ -685,9 +685,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) strvec_pushv(&args, diff_index_args); if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) BUG("malformed internal diff-index command line"); - result = run_diff_index(&revs, 0); + run_diff_index(&revs, 0); - if (!diff_result_code(&revs.diffopt, result)) + if (!diff_result_code(&revs.diffopt)) suffix = NULL; else suffix = dirty; diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 50330b8dd2..f38912cd40 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -80,14 +80,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) diff_merges_set_dense_combined_if_unset(&rev); - if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - result = -1; - goto cleanup; - } - result = run_diff_files(&rev, options); - result = diff_result_code(&rev.diffopt, result); -cleanup: + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) + die_errno("repo_read_index_preload"); + run_diff_files(&rev, options); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 9db7139b83..220f341ffa 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -72,8 +72,8 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) perror("repo_read_index"); return -1; } - result = run_diff_index(&rev, option); - result = diff_result_code(&rev.diffopt, result); + run_diff_index(&rev, option); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index c9ba35f143..86be634286 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -232,5 +232,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) diff_free(&opt->diffopt); } - return diff_result_code(&opt->diffopt, 0); + return diff_result_code(&opt->diffopt); } diff --git a/builtin/diff.c b/builtin/diff.c index b19530c996..0b313549c7 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -77,9 +77,9 @@ static void stuff_change(struct diff_options *opt, diff_queue(&diff_queued_diff, one, two); } -static int builtin_diff_b_f(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry **blob) +static void builtin_diff_b_f(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { /* Blob vs file in the working tree*/ struct stat st; @@ -109,12 +109,11 @@ static int builtin_diff_b_f(struct rev_info *revs, path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_blobs(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry **blob) +static void builtin_diff_blobs(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { const unsigned mode = canon_mode(S_IFREG | 0644); @@ -134,11 +133,10 @@ static int builtin_diff_blobs(struct rev_info *revs, blob_path(blob[0]), blob_path(blob[1])); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_index(struct rev_info *revs, - int argc, const char **argv) +static void builtin_diff_index(struct rev_info *revs, + int argc, const char **argv) { unsigned int option = 0; while (1 < argc) { @@ -163,20 +161,18 @@ static int builtin_diff_index(struct rev_info *revs, setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } } else if (repo_read_index(the_repository) < 0) { - perror("repo_read_cache"); - return -1; + die_errno("repo_read_cache"); } - return run_diff_index(revs, option); + run_diff_index(revs, option); } -static int builtin_diff_tree(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent0, - struct object_array_entry *ent1) +static void builtin_diff_tree(struct rev_info *revs, + int argc, const char **argv, + struct object_array_entry *ent0, + struct object_array_entry *ent1) { const struct object_id *(oid[2]); struct object_id mb_oid; @@ -209,13 +205,12 @@ static int builtin_diff_tree(struct rev_info *revs, } diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); log_tree_diff_flush(revs); - return 0; } -static int builtin_diff_combined(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry *ent, - int ents, int first_non_parent) +static void builtin_diff_combined(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry *ent, + int ents, int first_non_parent) { struct oid_array parents = OID_ARRAY_INIT; int i; @@ -236,7 +231,6 @@ static int builtin_diff_combined(struct rev_info *revs, } diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs); oid_array_clear(&parents); - return 0; } static void refresh_index_quietly(void) @@ -254,7 +248,7 @@ static void refresh_index_quietly(void) repo_update_index_if_able(the_repository, &lock_file); } -static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) +static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { unsigned int options = 0; @@ -269,8 +263,10 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv options |= DIFF_SILENT_ON_REMOVED; else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); - else - return error(_("invalid option: %s"), argv[1]); + else { + error(_("invalid option: %s"), argv[1]); + usage(builtin_diff_usage); + } argv++; argc--; } @@ -287,10 +283,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } - return run_diff_files(revs, options); + run_diff_files(revs, options); } struct symdiff { @@ -404,7 +399,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) int blobs = 0, paths = 0; struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; - int result = 0; + int result; struct symdiff sdiff; /* @@ -583,17 +578,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!ent.nr) { switch (blobs) { case 0: - result = builtin_diff_files(&rev, argc, argv); + builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); - result = builtin_diff_b_f(&rev, argc, argv, blob); + builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); - result = builtin_diff_blobs(&rev, argc, argv, blob); + builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); @@ -602,18 +597,18 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) - result = builtin_diff_index(&rev, argc, argv); + builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) { if (sdiff.warn) warning(_("%s...%s: multiple merge bases, using %s"), sdiff.left, sdiff.right, sdiff.base); - result = builtin_diff_tree(&rev, argc, argv, - &ent.objects[0], &ent.objects[1]); + builtin_diff_tree(&rev, argc, argv, + &ent.objects[0], &ent.objects[1]); } else - result = builtin_diff_combined(&rev, argc, argv, - ent.objects, ent.nr, - first_non_parent); - result = diff_result_code(&rev.diffopt, result); + builtin_diff_combined(&rev, argc, argv, + ent.objects, ent.nr, + first_non_parent); + result = diff_result_code(&rev.diffopt); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); release_revisions(&rev); diff --git a/builtin/log.c b/builtin/log.c index db3a88bfe9..87088077d9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -549,7 +549,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev) rev->diffopt.flags.check_failed) { return 02; } - return diff_result_code(&rev->diffopt, 0); + return diff_result_code(&rev->diffopt); } static int cmd_log_walk(struct rev_info *rev) @@ -1253,7 +1253,15 @@ static void show_diffstat(struct rev_info *rev, fprintf(rev->diffopt.file, "\n"); } +static void read_desc_file(struct strbuf *buf, const char *desc_file) +{ + if (strbuf_read_file(buf, desc_file, 0) < 0) + die_errno(_("unable to read branch description file '%s'"), + desc_file); +} + static void prepare_cover_text(struct pretty_print_context *pp, + const char *description_file, const char *branch_name, struct strbuf *sb, const char *encoding, @@ -1267,7 +1275,9 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (cover_from_description_mode == COVER_FROM_NONE) goto do_pp; - if (branch_name && *branch_name) + if (description_file && *description_file) + read_desc_file(&description_sb, description_file); + else if (branch_name && *branch_name) read_branch_desc(&description_sb, branch_name); if (!description_sb.len) goto do_pp; @@ -1313,6 +1323,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct commit *origin, int nr, struct commit **list, + const char *description_file, const char *branch_name, int quiet) { @@ -1352,7 +1363,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.rev = rev; pp.print_email_subject = 1; pp_user_info(&pp, NULL, &sb, committer, encoding); - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte); + prepare_cover_text(&pp, description_file, branch_name, &sb, + encoding, need_8bit_cte); fprintf(rev->diffopt.file, "%s\n", sb.buf); strbuf_release(&sb); @@ -1893,6 +1905,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int quiet = 0; const char *reroll_count = NULL; char *cover_from_description_arg = NULL; + char *description_file = NULL; char *branch_name = NULL; char *base_commit = NULL; struct base_tree_info bases; @@ -1936,6 +1949,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_STRING(0, "cover-from-description", &cover_from_description_arg, N_("cover-from-description-mode"), N_("generate parts of a cover letter based on a branch's description")), + OPT_FILENAME(0, "description-file", &description_file, + N_("use branch description from file")), OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"), N_("use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), @@ -2321,7 +2336,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet); print_bases(&bases, rev.diffopt.file); print_signature(rev.diffopt.file); total++; diff --git a/builtin/mv.c b/builtin/mv.c index 05e7156034..c596515ad0 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -305,7 +305,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } if (S_ISDIR(st.st_mode) && lstat(dst, &dest_st) == 0) { - bad = _("cannot move directory over file"); + bad = _("destination already exists"); goto act_on_entry; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index fb8e1549d1..8c4f0cb90a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -2527,10 +2527,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= receive_unpack_limit) + if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + else if (0 <= transfer_unpack_limit) + unpack_limit = transfer_unpack_limit; switch (determine_protocol_version_server()) { case protocol_v2: diff --git a/builtin/stash.c b/builtin/stash.c index fe64cde9ce..53e8868ba1 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -973,7 +973,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) } log_tree_diff_flush(&rev); - ret = diff_result_code(&rev.diffopt, 0); + ret = diff_result_code(&rev.diffopt); cleanup: strvec_clear(&stash_args); free_stash_info(&info); @@ -1089,7 +1089,6 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, */ static int check_changes_tracked_files(const struct pathspec *ps) { - int result; struct rev_info rev; struct object_id dummy; int ret = 0; @@ -1111,14 +1110,14 @@ static int check_changes_tracked_files(const struct pathspec *ps) add_head_to_pending(&rev); diff_setup_done(&rev.diffopt); - result = run_diff_index(&rev, 1); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_index(&rev, DIFF_INDEX_CACHED); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } - result = run_diff_files(&rev, 0); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_files(&rev, 0); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } @@ -1309,10 +1308,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps add_pending_object(&rev, parse_object(the_repository, &info->b_commit), ""); - if (run_diff_index(&rev, 0)) { - ret = -1; - goto done; - } + run_diff_index(&rev, 0); cp_upd_index.git_cmd = 1; strvec_pushl(&cp_upd_index.args, "update-index", diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f6871efd95..6f3bf33e61 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -629,7 +629,6 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, char *displaypath; struct strvec diff_files_args = STRVEC_INIT; struct rev_info rev = REV_INFO_INIT; - int diff_files_result; struct strbuf buf = STRBUF_INIT; const char *git_dir; struct setup_revision_opt opt = { @@ -669,9 +668,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = 0; setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt); - diff_files_result = run_diff_files(&rev, 0); + run_diff_files(&rev, 0); - if (!diff_result_code(&rev.diffopt, diff_files_result)) { + if (!diff_result_code(&rev.diffopt)) { print_status(flags, ' ', path, ce_oid, displaypath); } else if (!(flags & OPT_CACHED)) { @@ -1141,7 +1140,7 @@ static int compute_summary_module_list(struct object_id *head_oid, } if (diff_cmd == DIFF_INDEX) - run_diff_index(&rev, info->cached); + run_diff_index(&rev, info->cached ? DIFF_INDEX_CACHED : 0); else run_diff_files(&rev, 0); prepare_submodule_summary(info, &list); @@ -280,6 +280,8 @@ linux-leaks) ;; linux-asan-ubsan) export SANITIZE=address,undefined + export NO_SVN_TESTS=LetsSaveSomeTime + MAKEFLAGS="$MAKEFLAGS NO_PYTHON=YepBecauseP4FlakesTooOften" ;; esac diff --git a/commit-graph.c b/commit-graph.c index 0aa1640d15..9e6eaa8a46 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -128,6 +128,16 @@ timestamp_t commit_graph_generation(const struct commit *c) return GENERATION_NUMBER_INFINITY; } +static timestamp_t commit_graph_generation_from_graph(const struct commit *c) +{ + struct commit_graph_data *data = + commit_graph_data_slab_peek(&commit_graph_data_slab, c); + + if (!data || data->graph_pos == COMMIT_NOT_FROM_GRAPH) + return GENERATION_NUMBER_INFINITY; + return data->generation; +} + static struct commit_graph_data *commit_graph_data_at(const struct commit *c) { unsigned int i, nth_slab; @@ -2550,9 +2560,6 @@ static void graph_report(const char *fmt, ...) va_end(ap); } -#define GENERATION_ZERO_EXISTS 1 -#define GENERATION_NUMBER_EXISTS 2 - static int commit_graph_checksum_valid(struct commit_graph *g) { return hashfile_checksum_valid(g->data, g->data_len); @@ -2565,7 +2572,8 @@ static int verify_one_commit_graph(struct repository *r, { uint32_t i, cur_fanout_pos = 0; struct object_id prev_oid, cur_oid; - int generation_zero = 0; + struct commit *seen_gen_zero = NULL; + struct commit *seen_gen_non_zero = NULL; verify_commit_graph_error = verify_commit_graph_lite(g); if (verify_commit_graph_error) @@ -2659,7 +2667,7 @@ static int verify_one_commit_graph(struct repository *r, oid_to_hex(&graph_parents->item->object.oid), oid_to_hex(&odb_parents->item->object.oid)); - generation = commit_graph_generation(graph_parents->item); + generation = commit_graph_generation_from_graph(graph_parents->item); if (generation > max_generation) max_generation = generation; @@ -2671,16 +2679,12 @@ static int verify_one_commit_graph(struct repository *r, graph_report(_("commit-graph parent list for commit %s terminates early"), oid_to_hex(&cur_oid)); - if (!commit_graph_generation(graph_commit)) { - if (generation_zero == GENERATION_NUMBER_EXISTS) - graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"), - oid_to_hex(&cur_oid)); - generation_zero = GENERATION_ZERO_EXISTS; - } else if (generation_zero == GENERATION_ZERO_EXISTS) - graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"), - oid_to_hex(&cur_oid)); + if (commit_graph_generation_from_graph(graph_commit)) + seen_gen_non_zero = graph_commit; + else + seen_gen_zero = graph_commit; - if (generation_zero == GENERATION_ZERO_EXISTS) + if (seen_gen_zero) continue; /* @@ -2706,6 +2710,12 @@ static int verify_one_commit_graph(struct repository *r, odb_commit->date); } + if (seen_gen_zero && seen_gen_non_zero) + graph_report(_("commit-graph has both zero and non-zero " + "generations (e.g., commits '%s' and '%s')"), + oid_to_hex(&seen_gen_zero->object.oid), + oid_to_hex(&seen_gen_non_zero->object.oid)); + return verify_commit_graph_error; } diff --git a/diff-lib.c b/diff-lib.c index 6b0c6a7180..d8aa777a73 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -96,7 +96,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt, return changed; } -int run_diff_files(struct rev_info *revs, unsigned int option) +void run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; @@ -272,7 +272,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); trace_performance_since(start, "diff-files"); - return 0; } /* @@ -606,7 +605,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) free_commit_list(merge_bases); } -int run_diff_index(struct rev_info *revs, unsigned int option) +void run_diff_index(struct rev_info *revs, unsigned int option) { struct object_array_entry *ent; int cached = !!(option & DIFF_INDEX_CACHED); @@ -640,7 +639,6 @@ int run_diff_index(struct rev_info *revs, unsigned int option) diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); trace_performance_leave("diff-index"); - return 0; } int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt) @@ -682,7 +680,7 @@ int index_differs_from(struct repository *r, rev.diffopt.flags.ignore_submodules = flags->ignore_submodules; } rev.diffopt.ita_invisible_in_index = ita_invisible_in_index; - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); has_changes = rev.diffopt.flags.has_changes; release_revisions(&rev); return (has_changes != 0); diff --git a/diff-no-index.c b/diff-no-index.c index 4771cf02aa..8aead3e332 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -364,7 +364,7 @@ int diff_no_index(struct rev_info *revs, * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ - ret = diff_result_code(&revs->diffopt, 0); + ret = diff_result_code(&revs->diffopt); out: for (i = 0; i < ARRAY_SIZE(to_free); i++) @@ -3563,18 +3563,21 @@ static void builtin_diff(const char *name_a, strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset); if (xfrm_msg) strbuf_addstr(&header, xfrm_msg); + o->found_changes = 1; must_show_header = 1; } else if (lbl[1][0] == '/') { strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset); if (xfrm_msg) strbuf_addstr(&header, xfrm_msg); + o->found_changes = 1; must_show_header = 1; } else { if (one->mode != two->mode) { strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset); strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset); + o->found_changes = 1; must_show_header = 1; } if (xfrm_msg) @@ -4832,6 +4835,10 @@ void diff_setup_done(struct diff_options *options) else options->prefix_length = 0; + /* + * --name-only, --name-status, --checkdiff, and -s + * turn other output format off. + */ if (options->output_format & (DIFF_FORMAT_NAME | DIFF_FORMAT_NAME_STATUS | DIFF_FORMAT_CHECKDIFF | @@ -6206,6 +6213,8 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) fprintf(opt->file, "%s", diff_line_prefix(opt)); write_name_quoted(name_a, opt->file, opt->line_termination); } + + opt->found_changes = 1; } static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs) @@ -6684,6 +6693,21 @@ void diff_flush(struct diff_options *options) separator++; } + if (output_format & DIFF_FORMAT_PATCH) { + if (separator) { + emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); + if (options->stat_sep) + /* attach patch instead of inline */ + emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP, + NULL, 0, 0); + } + + diff_flush_patch_all_file_pairs(options); + } + + if (output_format & DIFF_FORMAT_CALLBACK) + options->format_callback(q, options, options->format_callback_data); + if (output_format & DIFF_FORMAT_NO_OUTPUT && options->flags.exit_with_status && options->flags.diff_from_contents) { @@ -6705,21 +6729,6 @@ void diff_flush(struct diff_options *options) } } - if (output_format & DIFF_FORMAT_PATCH) { - if (separator) { - emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); - if (options->stat_sep) - /* attach patch instead of inline */ - emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP, - NULL, 0, 0); - } - - diff_flush_patch_all_file_pairs(options); - } - - if (output_format & DIFF_FORMAT_CALLBACK) - options->format_callback(q, options, options->format_callback_data); - free_queue: diff_free_queue(q); DIFF_QUEUE_CLEAR(q); @@ -6973,16 +6982,14 @@ void diffcore_std(struct diff_options *options) options->found_follow = 0; } -int diff_result_code(struct diff_options *opt, int status) +int diff_result_code(struct diff_options *opt) { int result = 0; diff_warn_rename_limit("diff.renameLimit", opt->needed_rename_limit, opt->degraded_cc_to_c); - if (!opt->flags.exit_with_status && - !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) - return status; + if (opt->flags.exit_with_status && opt->flags.has_changes) result |= 01; @@ -7029,6 +7036,7 @@ void compute_diffstat(struct diff_options *options, if (check_pair_status(p)) diff_flush_stat(p, options, diffstat); } + options->found_changes = !!diffstat->nr; } void diff_addremove(struct diff_options *options, @@ -637,17 +637,17 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb); #define DIFF_SILENT_ON_REMOVED 01 /* report racily-clean paths as modified */ #define DIFF_RACY_IS_MODIFIED 02 -int run_diff_files(struct rev_info *revs, unsigned int option); +void run_diff_files(struct rev_info *revs, unsigned int option); #define DIFF_INDEX_CACHED 01 #define DIFF_INDEX_MERGE_BASE 02 -int run_diff_index(struct rev_info *revs, unsigned int option); +void run_diff_index(struct rev_info *revs, unsigned int option); int do_diff_cache(const struct object_id *, struct diff_options *); int diff_flush_patch_id(struct diff_options *, struct object_id *, int); void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx); -int diff_result_code(struct diff_options *, int); +int diff_result_code(struct diff_options *); int diff_no_index(struct rev_info *, int implicit_no_index, int, const char **); diff --git a/fetch-pack.c b/fetch-pack.c index 65c1ff4bb4..26999e3b65 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1911,10 +1911,10 @@ static void fetch_pack_setup(void) if (did_setup) return; fetch_pack_config(); - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= fetch_unpack_limit) + if (0 <= fetch_unpack_limit) unpack_limit = fetch_unpack_limit; + else if (0 <= transfer_unpack_limit) + unpack_limit = transfer_unpack_limit; did_setup = 1; } diff --git a/git-gui/Makefile b/git-gui/Makefile index a0d5a4b28e..3f80435436 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -138,25 +138,10 @@ GITGUI_SCRIPT := $$0 GITGUI_RELATIVE := GITGUI_MACOSXAPP := -ifeq ($(uname_O),Cygwin) - GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"` - - # Is this a Cygwin Tcl/Tk binary? If so it knows how to do - # POSIX path translation just like cygpath does and we must - # keep libdir in POSIX format so Cygwin packages of git-gui - # work no matter where the user installs them. - # - ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /)) - gg_libdir_sed_in := $(gg_libdir) - else - gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)") - endif -else - ifeq ($(exedir),$(gg_libdir)) - GITGUI_RELATIVE := 1 - endif - gg_libdir_sed_in := $(gg_libdir) +ifeq ($(exedir),$(gg_libdir)) + GITGUI_RELATIVE := 1 endif +gg_libdir_sed_in := $(gg_libdir) ifeq ($(uname_S),Darwin) ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y) GITGUI_MACOSXAPP := YesPlease diff --git a/git-gui/README.md b/git-gui/README.md index 5ce2122fbc..b460b649a8 100644 --- a/git-gui/README.md +++ b/git-gui/README.md @@ -88,7 +88,7 @@ that you first use `git-format-patch` to generate the emails, audit them, and then send them via `git-send-email`. A pretty good guide to configuring and using `git-send-email` can be found -[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/) +[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/). ### Using your email client diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 201524c34e..8bc8892c40 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -46,6 +46,132 @@ catch {rename send {}} ; # What an evil concept... ###################################################################### ## +## Enabling platform-specific code paths + +proc is_MacOSX {} { + if {[tk windowingsystem] eq {aqua}} { + return 1 + } + return 0 +} + +proc is_Windows {} { + if {$::tcl_platform(platform) eq {windows}} { + return 1 + } + return 0 +} + +set _iscygwin {} +proc is_Cygwin {} { + global _iscygwin + if {$_iscygwin eq {}} { + if {[string match "CYGWIN_*" $::tcl_platform(os)]} { + set _iscygwin 1 + } else { + set _iscygwin 0 + } + } + return $_iscygwin +} + +###################################################################### +## +## PATH lookup + +set _search_path {} +proc _which {what args} { + global env _search_exe _search_path + + if {$_search_path eq {}} { + if {[is_Windows]} { + set gitguidir [file dirname [info script]] + regsub -all ";" $gitguidir "\\;" gitguidir + set env(PATH) "$gitguidir;$env(PATH)" + set _search_path [split $env(PATH) {;}] + # Skip empty `PATH` elements + set _search_path [lsearch -all -inline -not -exact \ + $_search_path ""] + set _search_exe .exe + } else { + set _search_path [split $env(PATH) :] + set _search_exe {} + } + } + + if {[is_Windows] && [lsearch -exact $args -script] >= 0} { + set suffix {} + } else { + set suffix $_search_exe + } + + foreach p $_search_path { + set p [file join $p $what$suffix] + if {[file exists $p]} { + return [file normalize $p] + } + } + return {} +} + +proc sanitize_command_line {command_line from_index} { + set i $from_index + while {$i < [llength $command_line]} { + set cmd [lindex $command_line $i] + if {[file pathtype $cmd] ne "absolute"} { + set fullpath [_which $cmd] + if {$fullpath eq ""} { + throw {NOT-FOUND} "$cmd not found in PATH" + } + lset command_line $i $fullpath + } + + # handle piped commands, e.g. `exec A | B` + for {incr i} {$i < [llength $command_line]} {incr i} { + if {[lindex $command_line $i] eq "|"} { + incr i + break + } + } + } + return $command_line +} + +# Override `exec` to avoid unsafe PATH lookup + +rename exec real_exec + +proc exec {args} { + # skip options + for {set i 0} {$i < [llength $args]} {incr i} { + set arg [lindex $args $i] + if {$arg eq "--"} { + incr i + break + } + if {[string range $arg 0 0] ne "-"} { + break + } + } + set args [sanitize_command_line $args $i] + uplevel 1 real_exec $args +} + +# Override `open` to avoid unsafe PATH lookup + +rename open real_open + +proc open {args} { + set arg0 [lindex $args 0] + if {[string range $arg0 0 0] eq "|"} { + set command_line [string trim [string range $arg0 1 end]] + lset args 0 "| [sanitize_command_line $command_line 0]" + } + uplevel 1 real_open $args +} + +###################################################################### +## ## locate our library if { [info exists ::env(GIT_GUI_LIB_DIR) ] } { @@ -163,8 +289,6 @@ set _isbare {} set _gitexec {} set _githtmldir {} set _reponame {} -set _iscygwin {} -set _search_path {} set _shellpath {@@SHELL_PATH@@} set _trace [lsearch -exact $argv --trace] @@ -211,14 +335,7 @@ proc gitexec {args} { if {[catch {set _gitexec [git --exec-path]} err]} { error "Git not installed?\n\n$err" } - if {[is_Cygwin]} { - set _gitexec [exec cygpath \ - --windows \ - --absolute \ - $_gitexec] - } else { - set _gitexec [file normalize $_gitexec] - } + set _gitexec [file normalize $_gitexec] } if {$args eq {}} { return $_gitexec @@ -233,14 +350,7 @@ proc githtmldir {args} { # Git not installed or option not yet supported return {} } - if {[is_Cygwin]} { - set _githtmldir [exec cygpath \ - --windows \ - --absolute \ - $_githtmldir] - } else { - set _githtmldir [file normalize $_githtmldir] - } + set _githtmldir [file normalize $_githtmldir] } if {$args eq {}} { return $_githtmldir @@ -252,40 +362,6 @@ proc reponame {} { return $::_reponame } -proc is_MacOSX {} { - if {[tk windowingsystem] eq {aqua}} { - return 1 - } - return 0 -} - -proc is_Windows {} { - if {$::tcl_platform(platform) eq {windows}} { - return 1 - } - return 0 -} - -proc is_Cygwin {} { - global _iscygwin - if {$_iscygwin eq {}} { - if {$::tcl_platform(platform) eq {windows}} { - if {[catch {set p [exec cygpath --windir]} err]} { - set _iscygwin 0 - } else { - set _iscygwin 1 - # Handle MSys2 which is only cygwin when MSYSTEM is MSYS. - if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} { - set _iscygwin 0 - } - } - } else { - set _iscygwin 0 - } - } - return $_iscygwin -} - proc is_enabled {option} { global enabled_options if {[catch {set on $enabled_options($option)}]} {return 0} @@ -448,44 +524,6 @@ proc _git_cmd {name} { return $v } -proc _which {what args} { - global env _search_exe _search_path - - if {$_search_path eq {}} { - if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} { - set _search_path [split [exec cygpath \ - --windows \ - --path \ - --absolute \ - $env(PATH)] {;}] - set _search_exe .exe - } elseif {[is_Windows]} { - set gitguidir [file dirname [info script]] - regsub -all ";" $gitguidir "\\;" gitguidir - set env(PATH) "$gitguidir;$env(PATH)" - set _search_path [split $env(PATH) {;}] - set _search_exe .exe - } else { - set _search_path [split $env(PATH) :] - set _search_exe {} - } - } - - if {[is_Windows] && [lsearch -exact $args -script] >= 0} { - set suffix {} - } else { - set suffix $_search_exe - } - - foreach p $_search_path { - set p [file join $p $what$suffix] - if {[file exists $p]} { - return [file normalize $p] - } - } - return {} -} - # Test a file for a hashbang to identify executable scripts on Windows. proc is_shellscript {filename} { if {![file exists $filename]} {return 0} @@ -1259,9 +1297,6 @@ if {$_gitdir eq "."} { set _gitdir [pwd] } -if {![file isdirectory $_gitdir] && [is_Cygwin]} { - catch {set _gitdir [exec cygpath --windows $_gitdir]} -} if {![file isdirectory $_gitdir]} { catch {wm withdraw .} error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"] @@ -1273,11 +1308,7 @@ apply_config # v1.7.0 introduced --show-toplevel to return the canonical work-tree if {[package vcompare $_git_version 1.7.0] >= 0} { - if { [is_Cygwin] } { - catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]} - } else { - set _gitworktree [git rev-parse --show-toplevel] - } + set _gitworktree [git rev-parse --show-toplevel] } else { # try to set work tree from environment, core.worktree or use # cdup to obtain a relative path to the top of the worktree. If @@ -1502,24 +1533,8 @@ proc rescan {after {honor_trustmtime 1}} { } } -if {[is_Cygwin]} { - set is_git_info_exclude {} - proc have_info_exclude {} { - global is_git_info_exclude - - if {$is_git_info_exclude eq {}} { - if {[catch {exec test -f [gitdir info exclude]}]} { - set is_git_info_exclude 0 - } else { - set is_git_info_exclude 1 - } - } - return $is_git_info_exclude - } -} else { - proc have_info_exclude {} { - return [file readable [gitdir info exclude]] - } +proc have_info_exclude {} { + return [file readable [gitdir info exclude]] } proc rescan_stage2 {fd after} { @@ -2259,7 +2274,9 @@ proc do_git_gui {} { # Get the system-specific explorer app/command. proc get_explorer {} { - if {[is_Cygwin] || [is_Windows]} { + if {[is_Cygwin]} { + set explorer "/bin/cygstart.exe --explore" + } elseif {[is_Windows]} { set explorer "explorer.exe" } elseif {[is_MacOSX]} { set explorer "open" @@ -3053,10 +3070,6 @@ if {[is_MacOSX]} { set doc_path [githtmldir] if {$doc_path ne {}} { set doc_path [file join $doc_path index.html] - - if {[is_Cygwin]} { - set doc_path [exec cygpath --mixed $doc_path] - } } if {[file isfile $doc_path]} { @@ -4028,60 +4041,6 @@ set file_lists($ui_workdir) [list] wm title . "[appname] ([reponame]) [file normalize $_gitworktree]" focus -force $ui_comm -# -- Warn the user about environmental problems. Cygwin's Tcl -# does *not* pass its env array onto any processes it spawns. -# This means that git processes get none of our environment. -# -if {[is_Cygwin]} { - set ignored_env 0 - set suggest_user {} - set msg [mc "Possible environment issues exist. - -The following environment variables are probably -going to be ignored by any Git subprocess run -by %s: - -" [appname]] - foreach name [array names env] { - switch -regexp -- $name { - {^GIT_INDEX_FILE$} - - {^GIT_OBJECT_DIRECTORY$} - - {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} - - {^GIT_DIFF_OPTS$} - - {^GIT_EXTERNAL_DIFF$} - - {^GIT_PAGER$} - - {^GIT_TRACE$} - - {^GIT_CONFIG$} - - {^GIT_(AUTHOR|COMMITTER)_DATE$} { - append msg " - $name\n" - incr ignored_env - } - {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} { - append msg " - $name\n" - incr ignored_env - set suggest_user $name - } - } - } - if {$ignored_env > 0} { - append msg [mc " -This is due to a known issue with the -Tcl binary distributed by Cygwin."] - - if {$suggest_user ne {}} { - append msg [mc " - -A good replacement for %s -is placing values for the user.name and -user.email settings into your personal -~/.gitconfig file. -" $suggest_user] - } - warn_popup $msg - } - unset ignored_env msg suggest_user name -} - # -- Only initialize complex UI if we are going to stay running. # if {[is_enabled transport]} { diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index af1fee7c75..d23abedcb3 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -174,9 +174,6 @@ constructor pick {} { -foreground blue \ -underline 1 set home $::env(HOME) - if {[is_Cygwin]} { - set home [exec cygpath --windows --absolute $home] - } set home "[file normalize $home]/" set hlen [string length $home] foreach p $sorted_recent { @@ -374,18 +371,6 @@ proc _objdir {path} { return $objdir } - if {[is_Cygwin]} { - set objdir [file join $path .git objects.lnk] - if {[file isfile $objdir]} { - return [win32_read_lnk $objdir] - } - - set objdir [file join $path objects.lnk] - if {[file isfile $objdir]} { - return [win32_read_lnk $objdir] - } - } - return {} } @@ -623,12 +608,6 @@ method _do_clone2 {} { } set giturl $origin_url - if {[is_Cygwin] && [file isdirectory $giturl]} { - set giturl [exec cygpath --unix --absolute $giturl] - if {$clone_type eq {shared}} { - set objdir [exec cygpath --unix --absolute $objdir] - } - } if {[file exists $local_path]} { error_popup [mc "Location %s already exists." $local_path] @@ -668,11 +647,7 @@ method _do_clone2 {} { fconfigure $f_cp -translation binary -encoding binary cd $objdir while {[gets $f_in line] >= 0} { - if {[is_Cygwin]} { - puts $f_cp [exec cygpath --unix --absolute $line] - } else { - puts $f_cp [file normalize $line] - } + puts $f_cp [file normalize $line] } close $f_in close $f_cp diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl index 97d1d7aa02..674a41f5e0 100644 --- a/git-gui/lib/shortcut.tcl +++ b/git-gui/lib/shortcut.tcl @@ -27,13 +27,10 @@ proc do_windows_shortcut {} { } proc do_cygwin_shortcut {} { - global argv0 _gitworktree + global argv0 _gitworktree oguilib if {[catch { set desktop [exec cygpath \ - --windows \ - --absolute \ - --long-name \ --desktop] }]} { set desktop . @@ -48,19 +45,19 @@ proc do_cygwin_shortcut {} { set fn ${fn}.lnk } if {[catch { - set sh [exec cygpath \ - --windows \ - --absolute \ - /bin/sh.exe] - set me [exec cygpath \ - --unix \ - --absolute \ - $argv0] - win32_create_lnk $fn [list \ - $sh -c \ - "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \ - ] \ - [file normalize $_gitworktree] + set repodir [file normalize $_gitworktree] + set shargs {-c \ + "CHERE_INVOKING=1 \ + source /etc/profile; \ + git gui"} + exec /bin/mkshortcut.exe \ + --arguments $shargs \ + --desc "git-gui on $repodir" \ + --icon $oguilib/git-gui.ico \ + --name $fn \ + --show min \ + --workingdir $repodir \ + /bin/sh.exe } err]} { error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } diff --git a/parse-options.c b/parse-options.c index 76d2e76b49..e8e076c3a6 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1186,14 +1186,15 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t continue; } - for (cp = _(opts->help); *cp; cp = np) { + for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) { np = strchrnul(cp, '\n'); - usage_padding(outfile, pos); - fprintf(outfile, "%.*s\n", (int)(np - cp), cp); if (*np) np++; + usage_padding(outfile, pos); + fwrite(cp, 1, np - cp, outfile); pos = 0; } + fputc('\n', outfile); if (positive_name) { if (find_option_by_long_name(all_opts, positive_name)) @@ -409,6 +409,7 @@ static int cmd_clone(int argc, const char **argv) { const char *branch = NULL; int full_clone = 0, single_branch = 0, show_progress = isatty(2); + int src = 1; struct option clone_options[] = { OPT_STRING('b', "branch", &branch, N_("<branch>"), N_("branch to checkout after clone")), @@ -417,10 +418,13 @@ static int cmd_clone(int argc, const char **argv) OPT_BOOL(0, "single-branch", &single_branch, N_("only download metadata for the branch that will " "be checked out")), + OPT_BOOL(0, "src", &src, + N_("create repository within 'src' directory")), OPT_END(), }; const char * const clone_usage[] = { - N_("scalar clone [<options>] [--] <repo> [<dir>]"), + N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" + "\t[--[no-]src] <url> [<enlistment>]"), NULL }; const char *url; @@ -456,7 +460,10 @@ static int cmd_clone(int argc, const char **argv) if (is_directory(enlistment)) die(_("directory '%s' exists already"), enlistment); - dir = xstrfmt("%s/src", enlistment); + if (src) + dir = xstrfmt("%s/src", enlistment); + else + dir = xstrdup(enlistment); strbuf_reset(&buf); if (branch) @@ -657,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv) git_config(get_scalar_repos, &scalar_repos); for (i = 0; i < scalar_repos.nr; i++) { + int succeeded = 0; const char *dir = scalar_repos.items[i].string; strbuf_reset(&commondir); @@ -667,30 +675,56 @@ static int cmd_reconfigure(int argc, const char **argv) if (errno != ENOENT) { warning_errno(_("could not switch to '%s'"), dir); - res = -1; - continue; + goto loop_end; } strbuf_addstr(&buf, dir); if (remove_deleted_enlistment(&buf)) - res = error(_("could not remove stale " - "scalar.repo '%s'"), dir); - else - warning(_("removing stale scalar.repo '%s'"), + error(_("could not remove stale " + "scalar.repo '%s'"), dir); + else { + warning(_("removed stale scalar.repo '%s'"), dir); + succeeded = 1; + } strbuf_release(&buf); - } else if (discover_git_directory(&commondir, &gitdir) < 0) { - warning_errno(_("git repository gone in '%s'"), dir); - res = -1; - } else { - git_config_clear(); + goto loop_end; + } + + switch (discover_git_directory_reason(&commondir, &gitdir)) { + case GIT_DIR_INVALID_OWNERSHIP: + warning(_("repository at '%s' has different owner"), dir); + goto loop_end; + + case GIT_DIR_INVALID_GITFILE: + case GIT_DIR_INVALID_FORMAT: + warning(_("repository at '%s' has a format issue"), dir); + goto loop_end; + + case GIT_DIR_DISCOVERED: + succeeded = 1; + break; + + default: + warning(_("repository not found in '%s'"), dir); + break; + } - the_repository = &r; - r.commondir = commondir.buf; - r.gitdir = gitdir.buf; + git_config_clear(); - if (set_recommended_config(1) < 0) - res = -1; + the_repository = &r; + r.commondir = commondir.buf; + r.gitdir = gitdir.buf; + + if (set_recommended_config(1) >= 0) + succeeded = 1; + +loop_end: + if (!succeeded) { + res = -1; + warning(_("to unregister this repository from Scalar, run\n" + "\tgit config --global --unset --fixed-value scalar.repo \"%s\""), + dir); } } diff --git a/sequencer.c b/sequencer.c index 48475d1cc6..a66dcf8ab2 100644 --- a/sequencer.c +++ b/sequencer.c @@ -6229,7 +6229,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (checkout_onto(r, opts, onto_name, &oid, orig_head)) goto cleanup; - if (require_clean_work_tree(r, "rebase", "", 1, 1)) + if (require_clean_work_tree(r, "rebase", NULL, 1, 1)) goto cleanup; todo_list_write_total_nr(&new_todo); @@ -1221,19 +1221,6 @@ static const char *allowed_bare_repo_to_string( return NULL; } -enum discovery_result { - GIT_DIR_NONE = 0, - GIT_DIR_EXPLICIT, - GIT_DIR_DISCOVERED, - GIT_DIR_BARE, - /* these are errors */ - GIT_DIR_HIT_CEILING = -1, - GIT_DIR_HIT_MOUNT_POINT = -2, - GIT_DIR_INVALID_GITFILE = -3, - GIT_DIR_INVALID_OWNERSHIP = -4, - GIT_DIR_DISALLOWED_BARE = -5, -}; - /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. @@ -1385,21 +1372,23 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, } } -int discover_git_directory(struct strbuf *commondir, - struct strbuf *gitdir) +enum discovery_result discover_git_directory_reason(struct strbuf *commondir, + struct strbuf *gitdir) { struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT; size_t gitdir_offset = gitdir->len, cwd_len; size_t commondir_offset = commondir->len; struct repository_format candidate = REPOSITORY_FORMAT_INIT; + enum discovery_result result; if (strbuf_getcwd(&dir)) - return -1; + return GIT_DIR_CWD_FAILURE; cwd_len = dir.len; - if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) { + result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0); + if (result <= 0) { strbuf_release(&dir); - return -1; + return result; } /* @@ -1429,11 +1418,11 @@ int discover_git_directory(struct strbuf *commondir, strbuf_setlen(commondir, commondir_offset); strbuf_setlen(gitdir, gitdir_offset); clear_repository_format(&candidate); - return -1; + return GIT_DIR_INVALID_FORMAT; } clear_repository_format(&candidate); - return 0; + return result; } const char *setup_git_directory_gently(int *nongit_ok) @@ -1515,10 +1504,11 @@ const char *setup_git_directory_gently(int *nongit_ok) } *nongit_ok = 1; break; - case GIT_DIR_NONE: + case GIT_DIR_CWD_FAILURE: + case GIT_DIR_INVALID_FORMAT: /* * As a safeguard against setup_git_directory_gently_1 returning - * this value, fallthrough to BUG. Otherwise it is possible to + * these values, fallthrough to BUG. Otherwise it is possible to * set startup_info->have_repository to 1 when we did nothing to * find a repository. */ @@ -42,16 +42,45 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code); #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL) void setup_work_tree(void); + +/* + * discover_git_directory_reason() is similar to discover_git_directory(), + * except it returns an enum value instead. It is important to note that + * a zero-valued return here is actually GIT_DIR_NONE, which is different + * from discover_git_directory. + */ +enum discovery_result { + GIT_DIR_EXPLICIT = 1, + GIT_DIR_DISCOVERED = 2, + GIT_DIR_BARE = 3, + /* these are errors */ + GIT_DIR_HIT_CEILING = -1, + GIT_DIR_HIT_MOUNT_POINT = -2, + GIT_DIR_INVALID_GITFILE = -3, + GIT_DIR_INVALID_OWNERSHIP = -4, + GIT_DIR_DISALLOWED_BARE = -5, + GIT_DIR_INVALID_FORMAT = -6, + GIT_DIR_CWD_FAILURE = -7, +}; +enum discovery_result discover_git_directory_reason(struct strbuf *commondir, + struct strbuf *gitdir); + /* * Find the commondir and gitdir of the repository that contains the current * working directory, without changing the working directory or other global * state. The result is appended to commondir and gitdir. If the discovered * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will * both have the same result appended to the buffer. The return value is - * either 0 upon success and non-zero if no repository was found. + * either 0 upon success and -1 if no repository was found. */ -int discover_git_directory(struct strbuf *commondir, - struct strbuf *gitdir); +static inline int discover_git_directory(struct strbuf *commondir, + struct strbuf *gitdir) +{ + if (discover_git_directory_reason(commondir, gitdir) <= 0) + return -1; + return 0; +} + const char *setup_git_directory_gently(int *); const char *setup_git_directory(void); char *prefix_path(const char *prefix, int len, const char *path); diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index 4eebd9c2b5..83b83c9abb 100644 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -45,6 +45,7 @@ test_lazy_prereq GPG ' "$TEST_DIRECTORY"/lib-gpg/keyring.gpg && gpg --homedir "${GNUPGHOME}" --import-ownertrust \ "$TEST_DIRECTORY"/lib-gpg/ownertrust && + gpg --homedir "${GNUPGHOME}" --update-trustdb && gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \ --sign -u committer@example.com ;; diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh index 96ed3e1d69..39e92b0841 100755 --- a/t/perf/p2000-sparse-operations.sh +++ b/t/perf/p2000-sparse-operations.sh @@ -134,5 +134,6 @@ test_perf_on_all git diff-files -- $SPARSE_CONE/a test_perf_on_all git diff-tree HEAD test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a test_perf_on_all "git worktree add ../temp && git worktree remove ../temp" +test_perf_on_all git check-attr -a -- $SPARSE_CONE/a test_done diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index 8a95adf4b5..2a4f35e984 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -2259,4 +2259,76 @@ test_expect_success 'worktree is not expanded' ' ensure_not_expanded worktree remove .worktrees/hotfix ' +test_expect_success 'check-attr with pathspec inside sparse definition' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + run_on_all cp ../.gitattributes ./deep && + + test_all_match git check-attr -a -- deep/a && + + test_all_match git add deep/.gitattributes && + test_all_match git check-attr -a --cached -- deep/a +' + +test_expect_success 'check-attr with pathspec outside sparse definition' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + run_on_sparse mkdir folder1 && + run_on_all cp ../.gitattributes ./folder1 && + run_on_all cp a folder1/a && + + test_all_match git check-attr -a -- folder1/a && + + git -C full-checkout add folder1/.gitattributes && + test_sparse_match git add --sparse folder1/.gitattributes && + test_all_match git commit -m "add .gitattributes" && + test_sparse_match git sparse-checkout reapply && + test_all_match git check-attr -a --cached -- folder1/a +' + +# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due +# to an underlying issue in oneway_diff() within diff-lib.c. +# 'do_oneway_diff()' is not called as expected for paths that could match +# inside of a sparse directory. Specifically, the 'ce_path_match()' function +# fails to recognize files inside a sparse directory (e.g., when 'folder1/' +# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to +# proceed with 'do_oneway_diff()' if the pathspec could match inside of a +# sparse directory. +test_expect_failure 'diff --check with pathspec outside sparse definition' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo "a " >"$1" + EOF + + test_all_match git config core.whitespace -trailing-space,-space-before-tab && + + echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes && + run_on_all mkdir -p folder1 && + run_on_all cp ../.gitattributes ./folder1 && + test_all_match git add --sparse folder1/.gitattributes && + run_on_all ../edit-contents folder1/a && + test_all_match git add --sparse folder1/a && + + test_sparse_match git sparse-checkout reapply && + test_all_match test_must_fail git diff --check --cached -- folder1/a +' + +test_expect_success 'sparse-index is not expanded: check-attr' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + mkdir ./sparse-index/folder1 && + cp ./sparse-index/a ./sparse-index/folder1/a && + cp .gitattributes ./sparse-index/deep && + cp .gitattributes ./sparse-index/folder1 && + + git -C sparse-index add deep/.gitattributes && + git -C sparse-index add --sparse folder1/.gitattributes && + ensure_not_expanded check-attr -a --cached -- deep/a && + ensure_not_expanded check-attr -a --cached -- folder1/a +' + test_done diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh index 028d825e8f..36abdca5ee 100755 --- a/t/t3321-notes-stripspace.sh +++ b/t/t3321-notes-stripspace.sh @@ -5,6 +5,7 @@ test_description='Test commit notes with stripspace behavior' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh MULTI_LF="$LF$LF$LF" diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 3cf2b7a7fb..bc0e29b31a 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -1991,6 +1991,20 @@ test_expect_success 'cover letter using branch description (6)' ' grep hello actual ' +test_expect_success 'cover letter with --description-file' ' + test_when_finished "rm -f description.txt" && + cat >description.txt <<-\EOF && + subject from file + + body from file + EOF + git checkout rebuild-1 && + git format-patch --stdout --cover-letter --cover-from-description auto \ + --description-file description.txt main >actual && + grep "^Subject: \[PATCH 0/2\] subject from file$" actual && + grep "^body from file$" actual +' + test_expect_success 'cover letter with nothing' ' git format-patch --stdout --cover-letter >actual && test_line_count = 0 actual diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index b298f220e0..fcd2473e52 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -1,7 +1,7 @@ #!/bin/sh # # Copyright (c) 2006 Johannes E. Schindelin -# +# Copyright (c) 2023 Google LLC test_description='Test special whitespace in diff engine. @@ -11,6 +11,43 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh +for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \ + --raw! --name-only! --name-status! +do + opts=${opt_res%!} expect_failure= + test "$opts" = "$opt_res" || + expect_failure="test_expect_code 1" + + test_expect_success "status with $opts (different)" ' + echo foo >x && + git add x && + echo bar >x && + test_expect_code 1 git diff -w $opts --exit-code x + ' + + test_expect_success POSIXPERM "status with $opts (mode differs)" ' + test_when_finished "git update-index --chmod=-x x" && + echo foo >x && + git add x && + git update-index --chmod=+x x && + test_expect_code 1 git diff -w $opts --exit-code x + ' + + test_expect_success "status with $opts (removing an empty file)" ' + : >x && + git add x && + rm x && + test_expect_code 1 git diff -w $opts --exit-code -- x + ' + + test_expect_success "status with $opts (different but equivalent)" ' + echo foo >x && + git add x && + echo " foo" >x && + $expect_failure git diff -w $opts --exit-code x + ' +done + test_expect_success "Ray Lehtiniemi's example" ' cat <<-\EOF >x && do { diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh index 5bc28ad9f0..f439f469bd 100755 --- a/t/t4017-diff-retval.sh +++ b/t/t4017-diff-retval.sh @@ -138,4 +138,9 @@ test_expect_success 'check honors conflict marker length' ' git reset --hard ' +test_expect_success 'option errors are not confused by --exit-code' ' + test_must_fail git diff --exit-code --nonsense 2>err && + grep '^usage:' err +' + test_done diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh index e70e020ae9..eec3d73dc2 100755 --- a/t/t4040-whitespace-status.sh +++ b/t/t4040-whitespace-status.sh @@ -28,8 +28,7 @@ test_expect_success 'diff-tree --exit-code' ' test_expect_success 'diff-tree -b --exit-code' ' git diff -b --exit-code HEAD^ HEAD && - git diff-tree -b -p --exit-code HEAD^ HEAD && - git diff-tree -b --exit-code HEAD^ HEAD + git diff-tree -b -p --exit-code HEAD^ HEAD ' test_expect_success 'diff-index --cached --exit-code' ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 4df76173a8..ba65f17dd9 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -450,14 +450,15 @@ GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255)) GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256)) GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8)) GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10)) +GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16)) GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS)) GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN)) GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4)) GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3)) GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11)) +GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH)) GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12)) -GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16)) GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \ $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS)) GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4)) @@ -596,11 +597,6 @@ test_expect_success 'detect incorrect generation number' ' "generation for commit" ' -test_expect_success 'detect incorrect generation number' ' - corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \ - "commit-graph generation for commit" -' - test_expect_success 'detect incorrect commit date' ' corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \ "commit date" @@ -622,6 +618,16 @@ test_expect_success 'detect incorrect chunk count' ' $GRAPH_CHUNK_LOOKUP_OFFSET ' +test_expect_success 'detect mixed generation numbers (non-zero to zero)' ' + corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \ + "both zero and non-zero generations" +' + +test_expect_success 'detect mixed generation numbers (zero to non-zero)' ' + corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \ + "both zero and non-zero generations" +' + test_expect_success 'git fsck (checks commit-graph when config set to true)' ' git -C full fsck && corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \ diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 4f289063ce..19c36b57f4 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -1127,6 +1127,52 @@ do ' done +test_expect_success 'prepare source branch' ' + echo one >onebranch && + git checkout --orphan onebranch && + git rm --cached -r . && + git add onebranch && + git commit -m onebranch && + git rev-list --objects onebranch -- >actual && + # 3 objects should be created, at least ... + test 3 -le $(wc -l <actual) +' + +validate_store_type () { + git -C dest count-objects -v >actual && + case "$store_type" in + packed) + grep "^count: 0$" actual ;; + loose) + grep "^packs: 0$" actual ;; + esac || { + echo "store_type is $store_type" + cat actual + false + } +} + +test_unpack_limit () { + store_type=$1 + + case "$store_type" in + packed) fetch_limit=1 transfer_limit=10000 ;; + loose) fetch_limit=10000 transfer_limit=1 ;; + esac + + test_expect_success "fetch trumps transfer limit" ' + rm -fr dest && + git --bare init dest && + git -C dest config fetch.unpacklimit $fetch_limit && + git -C dest config transfer.unpacklimit $transfer_limit && + git -C dest fetch .. onebranch && + validate_store_type + ' +} + +test_unpack_limit packed +test_unpack_limit loose + setup_negotiation_tip () { SERVER="$1" URL="$2" diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh index eed3c9d81a..9fc9ba552f 100755 --- a/t/t5546-receive-limits.sh +++ b/t/t5546-receive-limits.sh @@ -9,10 +9,26 @@ TEST_PASSES_SANITIZE_LEAK=true # When the limit is 1, `git receive-pack` will call `git index-pack`. # When the limit is 10000, `git receive-pack` will call `git unpack-objects`. +validate_store_type () { + git -C dest count-objects -v >actual && + case "$store_type" in + index) + grep "^count: 0$" actual ;; + unpack) + grep "^packs: 0$" actual ;; + esac || { + echo "store_type is $store_type" + cat actual + false; + } +} + test_pack_input_limit () { - case "$1" in - index) unpack_limit=1 ;; - unpack) unpack_limit=10000 ;; + store_type=$1 + + case "$store_type" in + index) unpack_limit=1 other_limit=10000 ;; + unpack) unpack_limit=10000 other_limit=1 ;; esac test_expect_success 'prepare destination repository' ' @@ -43,6 +59,19 @@ test_pack_input_limit () { git --git-dir=dest config receive.maxInputSize 0 && git push dest HEAD ' + + test_expect_success 'prepare destination repository (once more)' ' + rm -fr dest && + git --bare init dest + ' + + test_expect_success 'receive trumps transfer' ' + git --git-dir=dest config receive.unpacklimit "$unpack_limit" && + git --git-dir=dest config transfer.unpacklimit "$other_limit" && + git push dest HEAD && + validate_store_type + ' + } test_expect_success "create known-size (1024 bytes) commit" ' diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh index a11b20e378..448134c4bf 100755 --- a/t/t5571-pre-push-hook.sh +++ b/t/t5571-pre-push-hook.sh @@ -4,6 +4,7 @@ test_description='check pre-push hooks' 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/t5583-push-branches.sh b/t/t5583-push-branches.sh index e7e1b6dab6..320f49c753 100755 --- a/t/t5583-push-branches.sh +++ b/t/t5583-push-branches.sh @@ -5,6 +5,7 @@ test_description='check the consisitency of behavior of --all and --branches' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh delete_refs() { diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 5b434ab451..aa3c7c03c4 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -1763,10 +1763,7 @@ test_expect_success GPGSSH 'setup for signature atom using ssh' ' ' test_expect_success GPG2 'bare signature atom' ' - git verify-commit first-signed 2>out.raw && - grep -Ev "checking the trustdb|PGP trust model" out.raw >out && - head -3 out >expect && - tail -1 out >>expect && + git verify-commit first-signed 2>expect && echo >>expect && git for-each-ref refs/tags/first-signed \ --format="%(signature)" >actual && diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh index 2d38a16480..bb95f09810 100755 --- a/t/t7516-commit-races.sh +++ b/t/t7516-commit-races.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='git commit races' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'race to create orphan commit' ' diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh index 872ad1c9c2..7869f45ee6 100755 --- a/t/t9211-scalar-clone.sh +++ b/t/t9211-scalar-clone.sh @@ -180,4 +180,16 @@ test_expect_success 'scalar clone warns when background maintenance fails' ' grep "could not turn on maintenance" err ' +test_expect_success '`scalar clone --no-src`' ' + scalar clone --src "file://$(pwd)/to-clone" with-src && + scalar clone --no-src "file://$(pwd)/to-clone" without-src && + + test_path_is_dir with-src/src && + test_path_is_missing without-src/src && + + (cd with-src/src && ls ?*) >with && + (cd without-src && ls ?*) >without && + test_cmp with without +' + test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index 293caf0f20..5ea5d1d62a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -334,6 +334,7 @@ nr_san_dir_leaks_ () { find "$TEST_RESULTS_SAN_DIR" \ -type f \ -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null | + xargs grep -lv "Unable to get registers from thread" | wc -l } diff --git a/wt-status.c b/wt-status.c index 5b1378965c..d03dfab9e4 100644 --- a/wt-status.c +++ b/wt-status.c @@ -675,7 +675,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.flags.recursive = 1; copy_pathspec(&rev.prune_data, &s->pathspec); - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); release_revisions(&rev); } @@ -1156,7 +1156,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.a_prefix = "c/"; rev.diffopt.b_prefix = "i/"; } /* else use prefix as per user config */ - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); if (s->verbose > 1 && wt_status_check_worktree_changes(s, &dirty_submodules)) { status_printf_ln(s, c, @@ -2580,8 +2580,8 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules) } rev_info.diffopt.flags.quick = 1; diff_setup_done(&rev_info.diffopt); - result = run_diff_files(&rev_info, 0); - result = diff_result_code(&rev_info.diffopt, result); + run_diff_files(&rev_info, 0); + result = diff_result_code(&rev_info.diffopt); release_revisions(&rev_info); return result; } @@ -2614,8 +2614,8 @@ int has_uncommitted_changes(struct repository *r, } diff_setup_done(&rev_info.diffopt); - result = run_diff_index(&rev_info, 1); - result = diff_result_code(&rev_info.diffopt, result); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); + result = diff_result_code(&rev_info.diffopt); release_revisions(&rev_info); return result; } @@ -2655,8 +2655,12 @@ int require_clean_work_tree(struct repository *r, } if (err) { - if (hint) + if (hint) { + if (!*hint) + BUG("empty hint passed to require_clean_work_tree();" + " use NULL instead"); error("%s", hint); + } if (!gently) exit(128); } |
