diff options
115 files changed, 1912 insertions, 541 deletions
@@ -151,7 +151,8 @@ Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch> Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)> Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de> Michael Coleman <tutufan@gmail.com> -Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm> +Michael J Gruber <git@grubix.eu> <michaeljgruber+gmane@fastmail.fm> +Michael J Gruber <git@grubix.eu> <git@drmicha.warpmail.net> Michael S. Tsirkin <mst@kernel.org> <mst@redhat.com> Michael S. Tsirkin <mst@kernel.org> <mst@mellanox.co.il> Michael S. Tsirkin <mst@kernel.org> <mst@dev.mellanox.co.il> diff --git a/.travis.yml b/.travis.yml index 9c63c8c3f6..591cc57b80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,6 +39,27 @@ env: matrix: include: + - env: Linux32 + os: linux + services: + - docker + before_install: + - docker pull daald/ubuntu32:xenial + before_script: + script: + - > + docker run + --interactive + --env DEFAULT_TEST_TARGET + --env GIT_PROVE_OPTS + --env GIT_TEST_OPTS + --env GIT_TEST_CLONE_2GB + --volume "${PWD}:/usr/src/git" + daald/ubuntu32:xenial + /usr/src/git/ci/run-linux32-build.sh $(id -u $USER) + # Use the following command to debug the docker build locally: + # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial + # root@container:/# /usr/src/git/ci/run-linux32-build.sh - env: Documentation os: linux compiler: clang diff --git a/Documentation/RelNotes/2.12.1.txt b/Documentation/RelNotes/2.12.1.txt new file mode 100644 index 0000000000..a74f7db747 --- /dev/null +++ b/Documentation/RelNotes/2.12.1.txt @@ -0,0 +1,41 @@ +Git v2.12.1 Release Notes +========================= + +Fixes since v2.12 +----------------- + + * Reduce authentication round-trip over HTTP when the server supports + just a single authentication method. This also improves the + behaviour when Git is misconfigured to enable http.emptyAuth + against a server that does not authenticate without a username + (i.e. not using Kerberos etc., which makes http.emptyAuth + pointless). + + * Windows port wants to use OpenSSL's implementation of SHA-1 + routines, so let them. + + * Add 32-bit Linux variant to the set of platforms to be tested with + Travis CI. + + * When a redirected http transport gets an error during the + redirected request, we ignored the error we got from the server, + and ended up giving a not-so-useful error message. + + * The patch subcommand of "git add -i" was meant to have paths + selection prompt just like other subcommand, unlike "git add -p" + directly jumps to hunk selection. Recently, this was broken and + "add -i" lost the paths selection dialog, but it now has been + fixed. + + * Git v2.12 was shipped with an embarrassing breakage where various + operations that verify paths given from the user stopped dying when + seeing an issue, and instead later triggering segfault. + + * The code to parse "git log -L..." command line was buggy when there + are many ranges specified with -L; overrun of the allocated buffer + has been fixed. + + * The command-line parsing of "git log -L" copied internal data + structures using incorrect size on ILP32 systems. + +Also contains various documentation updates and code clean-ups. diff --git a/Documentation/RelNotes/2.12.2.txt b/Documentation/RelNotes/2.12.2.txt new file mode 100644 index 0000000000..441939709c --- /dev/null +++ b/Documentation/RelNotes/2.12.2.txt @@ -0,0 +1,83 @@ +Git v2.12.2 Release Notes +========================= + +Fixes since v2.12.1 +------------------- + + * "git status --porcelain" is supposed to give a stable output, but a + few strings were left as translatable by mistake. + + * "Dumb http" transport used to misparse a nonsense http-alternates + response, which has been fixed. + + * "git diff --quiet" relies on the size field in diff_filespec to be + correctly populated, but diff_populate_filespec() helper function + made an incorrect short-cut when asked only to populate the size + field for paths that need to go through convert_to_git() (e.g. CRLF + conversion). + + * There is no need for Python only to give a few messages to the + standard error stream, but we somehow did. + + * A leak in a codepath to read from a packed object in (rare) cases + has been plugged. + + * "git upload-pack", which is a counter-part of "git fetch", did not + report a request for a ref that was not advertised as invalid. + This is generally not a problem (because "git fetch" will stop + before making such a request), but is the right thing to do. + + * A "gc.log" file left by a backgrounded "gc --auto" disables further + automatic gc; it has been taught to run at least once a day (by + default) by ignoring a stale "gc.log" file that is too old. + + * "git remote rm X", when a branch has remote X configured as the + value of its branch.*.remote, tried to remove branch.*.remote and + branch.*.merge and failed if either is unset. + + * A caller of tempfile API that uses stdio interface to write to + files may ignore errors while writing, which is detected when + tempfile is closed (with a call to ferror()). By that time, the + original errno that may have told us what went wrong is likely to + be long gone and was overwritten by an irrelevant value. + close_tempfile() now resets errno to EIO to make errno at least + predictable. + + * "git show-branch" expected there were only very short branch names + in the repository and used a fixed-length buffer to hold them + without checking for overflow. + + * The code that parses header fields in the commit object has been + updated for (micro)performance and code hygiene. + + * A test that creates a confusing branch whose name is HEAD has been + corrected not to do so. + + * "Cc:" on the trailer part does not have to conform to RFC strictly, + unlike in the e-mail header. "git send-email" has been updated to + ignore anything after '>' when picking addresses, to allow non-address + cruft like " # stable 4.4" after the address. + + * "git push" had a handful of codepaths that could lead to a deadlock + when unexpected error happened, which has been fixed. + + * Code to read submodule.<name>.ignore config did not state the + variable name correctly when giving an error message diagnosing + misconfiguration. + + * "git ls-remote" and "git archive --remote" are designed to work + without being in a directory under Git's control. However, recent + updates revealed that we randomly look into a directory called + .git/ without actually doing necessary set-up when working in a + repository. Stop doing so. + + * The code to parse the command line "git grep <patterns>... <rev> + [[--] <pathspec>...]" has been cleaned up, and a handful of bugs + have been fixed (e.g. we used to check "--" if it is a rev). + + * The code to parse "git -c VAR=VAL cmd" and set configuration + variable for the duration of cmd had two small bugs, which have + been fixed. + This supersedes jc/config-case-cmdline topic that has been discarded. + +Also contains various documentation updates and code clean-ups. diff --git a/Documentation/config.txt b/Documentation/config.txt index 015346c417..953cf69117 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1402,6 +1402,12 @@ gc.autoDetach:: Make `git gc --auto` return immediately and run in background if the system supports it. Default is true. +gc.logExpiry:: + If the file gc.log exists, then `git gc --auto` won't run + unless that file is more than 'gc.logExpiry' old. Default is + "1.day". See `gc.pruneExpire` for more ways to specify its + value. + gc.packRefs:: Running `git pack-refs` in a repository renders it unclonable by Git versions prior to 1.5.1.2 over dumb @@ -2432,6 +2438,8 @@ push.default:: pushing to the same repository you would normally pull from (i.e. central workflow). +* `tracking` - This is a deprecated synonym for `upstream`. + * `simple` - in centralized workflow, work like `upstream` with an added safety to refuse to push if the upstream branch's name is different from the local one. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 4f8f20a360..2763edb7a7 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -460,7 +460,7 @@ order). See linkgit:git-var[1] for details. HOOKS ----- This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`, -and `post-commit` hooks. See linkgit:githooks[5] for more +`post-commit` and `post-rewrite` hooks. See linkgit:githooks[5] for more information. FILES diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 0a09698c03..6e4bb02204 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -167,14 +167,12 @@ to other tags will be rewritten to point to the underlying commit. project root. Implies <<Remap_to_ancestor>>. --prune-empty:: - Some kind of filters will generate empty commits, that left the tree - untouched. This switch allow git-filter-branch to ignore such - commits. Though, this switch only applies for commits that have one - and only one parent, it will hence keep merges points. Also, this - option is not compatible with the use of `--commit-filter`. Though you - just need to use the function 'git_commit_non_empty_tree "$@"' instead - of the `git commit-tree "$@"` idiom in your commit filter to make that - happen. + Some filters will generate empty commits that leave the tree untouched. + This option instructs git-filter-branch to remove such commits if they + have exactly one or zero non-pruned parents; merge commits will + therefore remain intact. This option cannot be used together with + `--commit-filter`, though the same effect can be achieved by using the + provided `git_commit_non_empty_tree` function in a commit filter. --original <namespace>:: Use this option to set the namespace where the original commits diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 9b200b379b..f7a069bb92 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -239,7 +239,7 @@ keeping them as Git notes allows them to be maintained between versions of the patch series (but see the discussion of the `notes.rewrite` configuration options in linkgit:git-notes[1] to use this workflow). ---[no]-signature=<signature>:: +--[no-]signature=<signature>:: Add a signature to each message produced. Per RFC 3676 the signature is separated from the body by a line with '-- ' on it. If the signature option is omitted the signature defaults to the Git version diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 642d0ef199..9d66166f69 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -89,7 +89,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`. reply to the given Message-Id, which avoids breaking threads to provide a new patch series. The second and subsequent emails will be sent as replies according to - the `--[no]-chain-reply-to` setting. + the `--[no-]chain-reply-to` setting. + So for example when `--thread` and `--no-chain-reply-to` are specified, the second and subsequent patches will be replies to the first one like in the diff --git a/Documentation/git.txt b/Documentation/git.txt index aa895da4a5..88c39d3ed5 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,9 +44,11 @@ unreleased) version of Git, that is available from the 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v2.12.0/git.html[documentation for release 2.12.0] +* link:v2.12.2/git.html[documentation for release 2.12.2] * release notes for + link:RelNotes/2.12.2.txt[2.12.2]. + link:RelNotes/2.12.1.txt[2.12.1]. link:RelNotes/2.12.0.txt[2.12]. * link:v2.11.1/git.html[documentation for release 2.11.1] diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt index 46bc6d077c..c0a60f3158 100644 --- a/Documentation/gitdiffcore.txt +++ b/Documentation/gitdiffcore.txt @@ -84,8 +84,8 @@ format sections of the manual for 'git diff-{asterisk}' commands) or diff-patch format. -diffcore-break: For Splitting Up "Complete Rewrites" ----------------------------------------------------- +diffcore-break: For Splitting Up Complete Rewrites +-------------------------------------------------- The second transformation in the chain is diffcore-break, and is controlled by the -B option to the 'git diff-{asterisk}' commands. This is @@ -119,7 +119,7 @@ the original is used), and can be customized by giving a number after "-B" option (e.g. "-B75" to tell it to use 75%). -diffcore-rename: For Detection Renames and Copies +diffcore-rename: For Detecting Renames and Copies ------------------------------------------------- This transformation is used to detect renames and copies, and is @@ -177,8 +177,8 @@ the expense of making it slower. Without `--find-copies-harder`, copied happened to have been modified in the same changeset. -diffcore-merge-broken: For Putting "Complete Rewrites" Back Together --------------------------------------------------------------------- +diffcore-merge-broken: For Putting Complete Rewrites Back Together +------------------------------------------------------------------ This transformation is used to merge filepairs broken by diffcore-break, and not transformed into rename/copy by diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 6a208e92bf..e83f591a7b 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.12.0 +DEF_VER=v2.12.2 LF=' ' @@ -102,8 +102,6 @@ all:: # # Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash. # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -# # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd # in the C library. # @@ -1280,9 +1278,6 @@ ifdef MKDIR_WO_TRAILING_SLASH COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH COMPAT_OBJS += compat/mkdir.o endif -ifdef NO_MKSTEMPS - COMPAT_CFLAGS += -DNO_MKSTEMPS -endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o @@ -2254,6 +2249,9 @@ endif ifdef GIT_PERF_MAKE_OPTS @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+ endif +ifdef GIT_INTEROP_MAKE_OPTS + @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+ +endif ifdef TEST_GIT_INDEX_VERSION @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+ endif @@ -12,7 +12,7 @@ Torvalds with help of a group of hackers around the net. Please read the file [INSTALL][] for installation instructions. -Many Git online resources are accessible from http://git-scm.com/ +Many Git online resources are accessible from <https://git-scm.com/> including full documentation and Git related tools. See [Documentation/gittutorial.txt][] to get started, then see @@ -33,8 +33,8 @@ requests, comments and patches to git@vger.kernel.org (read [Documentation/SubmittingPatches][] for instructions on patch submission). To subscribe to the list, send an email with just "subscribe git" in the body to majordomo@vger.kernel.org. The mailing list archives are -available at https://public-inbox.org/git, -http://marc.info/?l=git and other archival sites. +available at <https://public-inbox.org/git/>, +<http://marc.info/?l=git> and other archival sites. The maintainer frequently sends the "What's cooking" reports that list the current status of various development topics to the mailing @@ -1 +1 @@ -Documentation/RelNotes/2.12.0.txt
\ No newline at end of file +Documentation/RelNotes/2.12.2.txt
\ No newline at end of file @@ -214,12 +214,12 @@ const char *real_path_if_valid(const char *path) return strbuf_realpath(&realpath, path, 0); } -char *real_pathdup(const char *path) +char *real_pathdup(const char *path, int die_on_error) { struct strbuf realpath = STRBUF_INIT; char *retval = NULL; - if (strbuf_realpath(&realpath, path, 0)) + if (strbuf_realpath(&realpath, path, die_on_error)) retval = strbuf_detach(&realpath, NULL); strbuf_release(&realpath); @@ -940,7 +940,7 @@ int bisect_next_all(const char *prefix, int no_checkout) struct commit_list *tried; int reaches = 0, all = 0, nr, steps; const unsigned char *bisect_rev; - char steps_msg[32]; + char *steps_msg; read_bisect_terms(&term_bad, &term_good); if (read_bisect_refs()) @@ -990,14 +990,15 @@ int bisect_next_all(const char *prefix, int no_checkout) nr = all - reaches - 1; steps = estimate_bisect_steps(all); - xsnprintf(steps_msg, sizeof(steps_msg), - Q_("(roughly %d step)", "(roughly %d steps)", steps), - steps); + + steps_msg = xstrfmt(Q_("(roughly %d step)", "(roughly %d steps)", + steps), steps); /* TRANSLATORS: the last %s will be replaced with "(roughly %d steps)" translation */ printf(Q_("Bisecting: %d revision left to test after this %s\n", "Bisecting: %d revisions left to test after this %s\n", nr), nr, steps_msg); + free(steps_msg); return bisect_checkout(bisect_rev, no_checkout); } diff --git a/builtin/blame.c b/builtin/blame.c index cffc626540..f7aa95f4ba 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -658,8 +658,11 @@ static struct origin *find_rename(struct scoreboard *sb, /* * Append a new blame entry to a given output queue. */ -static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e) +static void add_blame_entry(struct blame_entry ***queue, + const struct blame_entry *src) { + struct blame_entry *e = xmalloc(sizeof(*e)); + memcpy(e, src, sizeof(*e)); origin_incref(e->suspect); e->next = **queue; @@ -760,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed, struct blame_entry *split, struct blame_entry *e) { - struct blame_entry *new_entry; - if (split[0].suspect && split[2].suspect) { /* The first part (reuse storage for the existing entry e) */ dup_entry(unblamed, e, &split[0]); /* The last part -- me */ - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[2]), sizeof(struct blame_entry)); - add_blame_entry(unblamed, new_entry); + add_blame_entry(unblamed, &split[2]); /* ... and the middle part -- parent */ - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[1]), sizeof(struct blame_entry)); - add_blame_entry(blamed, new_entry); + add_blame_entry(blamed, &split[1]); } else if (!split[0].suspect && !split[2].suspect) /* @@ -785,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed, else if (split[0].suspect) { /* me and then parent */ dup_entry(unblamed, e, &split[0]); - - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[1]), sizeof(struct blame_entry)); - add_blame_entry(blamed, new_entry); + add_blame_entry(blamed, &split[1]); } else { /* parent and then me */ dup_entry(blamed, e, &split[1]); - - new_entry = xmalloc(sizeof(*new_entry)); - memcpy(new_entry, &(split[2]), sizeof(struct blame_entry)); - add_blame_entry(unblamed, new_entry); + add_blame_entry(unblamed, &split[2]); } } diff --git a/builtin/branch.c b/builtin/branch.c index 9d30f55b0b..babfd0f730 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -190,17 +190,20 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int ret = 0; int remote_branch = 0; struct strbuf bname = STRBUF_INIT; + unsigned allowed_interpret; switch (kinds) { case FILTER_REFS_REMOTES: fmt = "refs/remotes/%s"; /* For subsequent UI messages */ remote_branch = 1; + allowed_interpret = INTERPRET_BRANCH_REMOTE; force = 1; break; case FILTER_REFS_BRANCHES: fmt = "refs/heads/%s"; + allowed_interpret = INTERPRET_BRANCH_LOCAL; break; default: die(_("cannot use -a with -d")); @@ -215,7 +218,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, char *target = NULL; int flags = 0; - strbuf_branchname(&bname, argv[i]); + strbuf_branchname(&bname, argv[i], allowed_interpret); free(name); name = mkpathdup(fmt, bname.buf); diff --git a/builtin/checkout.c b/builtin/checkout.c index f174f50303..81f07c3ef2 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - strbuf_branchname(&buf, branch->name); + strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL); if (strcmp(buf.buf, branch->name)) branch->name = xstrdup(buf.buf); strbuf_splice(&buf, 0, 0, "refs/heads/", 11); diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index cfe9e447c2..2a1c1c213f 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -219,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix) * remote no-such-ref' would silently succeed without issuing * an error. */ - for (i = 0; i < nr_sought; i++) { - if (!sought[i] || sought[i]->matched) - continue; - error("no such remote ref %s", sought[i]->name); - ret = 1; - } + ret |= report_unmatched_refs(sought, nr_sought); while (ref) { printf("%s %s\n", diff --git a/builtin/gc.c b/builtin/gc.c index 331f219260..a2b9e8924e 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -33,6 +33,8 @@ static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static int detach_auto = 1; +static unsigned long gc_log_expire_time; +static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; @@ -76,10 +78,28 @@ static void git_config_date_string(const char *key, const char **output) static void process_log_file(void) { struct stat st; - if (!fstat(get_lock_file_fd(&log_lock), &st) && st.st_size) + if (fstat(get_lock_file_fd(&log_lock), &st)) { + /* + * Perhaps there was an i/o error or another + * unlikely situation. Try to make a note of + * this in gc.log along with any existing + * messages. + */ + int saved_errno = errno; + fprintf(stderr, _("Failed to fstat %s: %s"), + get_tempfile_path(&log_lock.tempfile), + strerror(saved_errno)); + fflush(stderr); commit_lock_file(&log_lock); - else + errno = saved_errno; + } else if (st.st_size) { + /* There was some error recorded in the lock file */ + commit_lock_file(&log_lock); + } else { + /* No error, clean up any old gc.log */ + unlink(git_path("gc.log")); rollback_lock_file(&log_lock); + } } static void process_log_file_at_exit(void) @@ -113,6 +133,8 @@ static void gc_config(void) git_config_get_bool("gc.autodetach", &detach_auto); git_config_date_string("gc.pruneexpire", &prune_expire); git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire); + git_config_date_string("gc.logexpiry", &gc_log_expire); + git_config(git_default_config, NULL); } @@ -290,19 +312,34 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) static int report_last_gc_error(void) { struct strbuf sb = STRBUF_INIT; - int ret; + int ret = 0; + struct stat st; + char *gc_log_path = git_pathdup("gc.log"); - ret = strbuf_read_file(&sb, git_path("gc.log"), 0); + if (stat(gc_log_path, &st)) { + if (errno == ENOENT) + goto done; + + ret = error_errno(_("Can't stat %s"), gc_log_path); + goto done; + } + + if (st.st_mtime < gc_log_expire_time) + goto done; + + ret = strbuf_read_file(&sb, gc_log_path, 0); if (ret > 0) - return error(_("The last gc run reported the following. " + ret = error(_("The last gc run reported the following. " "Please correct the root cause\n" "and remove %s.\n" "Automatic cleanup will not be performed " "until the file is removed.\n\n" "%s"), - git_path("gc.log"), sb.buf); + gc_log_path, sb.buf); strbuf_release(&sb); - return 0; +done: + free(gc_log_path); + return ret; } static int gc_before_repack(void) @@ -349,7 +386,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); + /* default expiry time, overwritten in gc_config */ gc_config(); + if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) + die(_("Failed to parse gc.logexpiry value %s"), gc_log_expire); if (pack_refs < 0) pack_refs = !is_bare_repository(); @@ -448,5 +488,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) warning(_("There are too many unreachable loose objects; " "run 'git prune' to remove them.")); + if (!daemonized) + unlink(git_path("gc.log")); + return 0; } diff --git a/builtin/grep.c b/builtin/grep.c index 2c727ef499..9304c33e75 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -967,6 +967,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) int dummy; int use_index = 1; int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; + int allow_revs; struct option options[] = { OPT_BOOL(0, "cached", &cached, @@ -1149,26 +1150,69 @@ int cmd_grep(int argc, const char **argv, const char *prefix) compile_grep_patterns(&opt); - /* Check revs and then paths */ + /* + * We have to find "--" in a separate pass, because its presence + * influences how we will parse arguments that come before it. + */ + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + seen_dashdash = 1; + break; + } + } + + /* + * Resolve any rev arguments. If we have a dashdash, then everything up + * to it must resolve as a rev. If not, then we stop at the first + * non-rev and assume everything else is a path. + */ + allow_revs = use_index && !untracked; for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; struct object_context oc; - /* Is it a rev? */ - if (!get_sha1_with_context(arg, 0, sha1, &oc)) { - struct object *object = parse_object_or_die(sha1, arg); - if (!seen_dashdash) - verify_non_filename(prefix, arg); - add_object_array_with_path(object, arg, &list, oc.mode, oc.path); - continue; - } + struct object *object; + if (!strcmp(arg, "--")) { i++; - seen_dashdash = 1; + break; } - break; + + if (!allow_revs) { + if (seen_dashdash) + die(_("--no-index or --untracked cannot be used with revs")); + break; + } + + if (get_sha1_with_context(arg, 0, sha1, &oc)) { + if (seen_dashdash) + die(_("unable to resolve revision: %s"), arg); + break; + } + + object = parse_object_or_die(sha1, arg); + if (!seen_dashdash) + verify_non_filename(prefix, arg); + add_object_array_with_path(object, arg, &list, oc.mode, oc.path); } + /* + * Anything left over is presumed to be a path. But in the non-dashdash + * "do what I mean" case, we verify and complain when that isn't true. + */ + if (!seen_dashdash) { + int j; + for (j = i; j < argc; j++) + verify_filename(prefix, argv[j], j == i && allow_revs); + } + + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD | + (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), + prefix, argv + i); + pathspec.max_depth = opt.max_depth; + pathspec.recursive = 1; + #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) num_threads = 0; @@ -1190,20 +1234,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } #endif - /* The rest are paths */ - if (!seen_dashdash) { - int j; - for (j = i; j < argc; j++) - verify_filename(prefix, argv[j], j == i); - } - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD | - (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), - prefix, argv + i); - pathspec.max_depth = opt.max_depth; - pathspec.recursive = 1; - if (recurse_submodules) { gitmodules_config(); compile_submodule_options(&opt, &pathspec, cached, untracked, @@ -1245,8 +1275,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!use_index || untracked) { int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; - if (list.nr) - die(_("--no-index or --untracked cannot be used with revs.")); hit = grep_directory(&opt, &pathspec, use_exclude, use_index); } else if (0 <= opt_exclude) { die(_("--[no-]exclude-standard cannot be used for tracked contents.")); diff --git a/builtin/init-db.c b/builtin/init-db.c index 1d4d6a0078..8a6acb0ec6 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -338,7 +338,7 @@ int init_db(const char *git_dir, const char *real_git_dir, { int reinit; int exist_ok = flags & INIT_DB_EXIST_OK; - char *original_git_dir = real_pathdup(git_dir); + char *original_git_dir = real_pathdup(git_dir, 1); if (real_git_dir) { struct stat st; @@ -489,7 +489,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); if (real_git_dir && !is_absolute_path(real_git_dir)) - real_git_dir = real_pathdup(real_git_dir); + real_git_dir = real_pathdup(real_git_dir, 1); if (argc == 1) { int mkdir_tried = 0; @@ -560,7 +560,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir_parent = strrchr(git_dir, '/'); if (git_dir_parent) { char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = real_pathdup(rel); + git_work_tree_cfg = real_pathdup(rel, 1); free(rel); } if (!git_work_tree_cfg) diff --git a/builtin/merge.c b/builtin/merge.c index a96d4fb501..848a298556 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -438,7 +438,7 @@ static void merge_name(const char *remote, struct strbuf *msg) char *found_ref; int len, early; - strbuf_branchname(&bname, remote); + strbuf_branchname(&bname, remote, 0); remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8841f8b366..c7af475485 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1539,6 +1539,8 @@ static int pack_offset_sort(const void *_a, const void *_b) * 2. Updating our size/type to the non-delta representation. These were * either not recorded initially (size) or overwritten with the delta type * (type) when check_object() decided to reuse the delta. + * + * 3. Resetting our delta depth, as we are now a base object. */ static void drop_reused_delta(struct object_entry *entry) { @@ -1552,6 +1554,7 @@ static void drop_reused_delta(struct object_entry *entry) p = &(*p)->delta_sibling; } entry->delta = NULL; + entry->depth = 0; oi.sizep = &entry->size; oi.typep = &entry->type; @@ -1570,39 +1573,123 @@ static void drop_reused_delta(struct object_entry *entry) * Follow the chain of deltas from this entry onward, throwing away any links * that cause us to hit a cycle (as determined by the DFS state flags in * the entries). + * + * We also detect too-long reused chains that would violate our --depth + * limit. */ static void break_delta_chains(struct object_entry *entry) { - /* If it's not a delta, it can't be part of a cycle. */ - if (!entry->delta) { - entry->dfs_state = DFS_DONE; - return; - } + /* + * The actual depth of each object we will write is stored as an int, + * as it cannot exceed our int "depth" limit. But before we break + * changes based no that limit, we may potentially go as deep as the + * number of objects, which is elsewhere bounded to a uint32_t. + */ + uint32_t total_depth; + struct object_entry *cur, *next; + + for (cur = entry, total_depth = 0; + cur; + cur = cur->delta, total_depth++) { + if (cur->dfs_state == DFS_DONE) { + /* + * We've already seen this object and know it isn't + * part of a cycle. We do need to append its depth + * to our count. + */ + total_depth += cur->depth; + break; + } - switch (entry->dfs_state) { - case DFS_NONE: /* - * This is the first time we've seen the object. We mark it as - * part of the active potential cycle and recurse. + * We break cycles before looping, so an ACTIVE state (or any + * other cruft which made its way into the state variable) + * is a bug. */ - entry->dfs_state = DFS_ACTIVE; - break_delta_chains(entry->delta); - entry->dfs_state = DFS_DONE; - break; + if (cur->dfs_state != DFS_NONE) + die("BUG: confusing delta dfs state in first pass: %d", + cur->dfs_state); - case DFS_DONE: - /* object already examined, and not part of a cycle */ - break; + /* + * Now we know this is the first time we've seen the object. If + * it's not a delta, we're done traversing, but we'll mark it + * done to save time on future traversals. + */ + if (!cur->delta) { + cur->dfs_state = DFS_DONE; + break; + } - case DFS_ACTIVE: /* - * We found a cycle that needs broken. It would be correct to - * break any link in the chain, but it's convenient to - * break this one. + * Mark ourselves as active and see if the next step causes + * us to cycle to another active object. It's important to do + * this _before_ we loop, because it impacts where we make the + * cut, and thus how our total_depth counter works. + * E.g., We may see a partial loop like: + * + * A -> B -> C -> D -> B + * + * Cutting B->C breaks the cycle. But now the depth of A is + * only 1, and our total_depth counter is at 3. The size of the + * error is always one less than the size of the cycle we + * broke. Commits C and D were "lost" from A's chain. + * + * If we instead cut D->B, then the depth of A is correct at 3. + * We keep all commits in the chain that we examined. */ - drop_reused_delta(entry); - entry->dfs_state = DFS_DONE; - break; + cur->dfs_state = DFS_ACTIVE; + if (cur->delta->dfs_state == DFS_ACTIVE) { + drop_reused_delta(cur); + cur->dfs_state = DFS_DONE; + break; + } + } + + /* + * And now that we've gone all the way to the bottom of the chain, we + * need to clear the active flags and set the depth fields as + * appropriate. Unlike the loop above, which can quit when it drops a + * delta, we need to keep going to look for more depth cuts. So we need + * an extra "next" pointer to keep going after we reset cur->delta. + */ + for (cur = entry; cur; cur = next) { + next = cur->delta; + + /* + * We should have a chain of zero or more ACTIVE states down to + * a final DONE. We can quit after the DONE, because either it + * has no bases, or we've already handled them in a previous + * call. + */ + if (cur->dfs_state == DFS_DONE) + break; + else if (cur->dfs_state != DFS_ACTIVE) + die("BUG: confusing delta dfs state in second pass: %d", + cur->dfs_state); + + /* + * If the total_depth is more than depth, then we need to snip + * the chain into two or more smaller chains that don't exceed + * the maximum depth. Most of the resulting chains will contain + * (depth + 1) entries (i.e., depth deltas plus one base), and + * the last chain (i.e., the one containing entry) will contain + * whatever entries are left over, namely + * (total_depth % (depth + 1)) of them. + * + * Since we are iterating towards decreasing depth, we need to + * decrement total_depth as we go, and we need to write to the + * entry what its final depth will be after all of the + * snipping. Since we're snipping into chains of length (depth + * + 1) entries, the final depth of an entry will be its + * original depth modulo (depth + 1). Any time we encounter an + * entry whose final depth is supposed to be zero, we snip it + * from its delta base, thereby making it so. + */ + cur->depth = (total_depth--) % (depth + 1); + if (!cur->depth) + drop_reused_delta(cur); + + cur->dfs_state = DFS_DONE; } } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 1dbb8a0692..8672825e2e 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1664,8 +1664,11 @@ static const char *unpack(int err_fd, struct shallow_info *si) } tmp_objdir = tmp_objdir_create(); - if (!tmp_objdir) + if (!tmp_objdir) { + if (err_fd > 0) + close(err_fd); return "unable to create temporary object directory"; + } child.env = tmp_objdir_env(tmp_objdir); /* diff --git a/builtin/remote.c b/builtin/remote.c index 5339ed6ad1..7682206c1e 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -769,7 +769,9 @@ static int rm(int argc, const char **argv) strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.%s", item->string, *k); - git_config_set(buf.buf, NULL); + result = git_config_set_gently(buf.buf, NULL); + if (result && result != CONFIG_NOTHING_SET) + die(_("could not unset '%s'"), buf.buf); } } } diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 974f3403ab..19756595d5 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -275,8 +275,7 @@ static void show_one_commit(struct commit *commit, int no_name) pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); pretty_str = pretty.buf; } - if (starts_with(pretty_str, "[PATCH] ")) - pretty_str += 8; + skip_prefix(pretty_str, "[PATCH] ", &pretty_str); if (!no_name) { if (name && name->head_name) { @@ -470,18 +469,14 @@ static void snarf_refs(int head, int remotes) } } -static int rev_is_head(char *head, int headlen, char *name, +static int rev_is_head(const char *head, const char *name, unsigned char *head_sha1, unsigned char *sha1) { - if ((!head[0]) || - (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) + if (!head || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) return 0; - if (starts_with(head, "refs/heads/")) - head += 11; - if (starts_with(name, "refs/heads/")) - name += 11; - else if (starts_with(name, "heads/")) - name += 6; + skip_prefix(head, "refs/heads/", &head); + if (!skip_prefix(name, "refs/heads/", &name)) + skip_prefix(name, "heads/", &name); return !strcmp(head, name); } @@ -620,9 +615,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int all_heads = 0, all_remotes = 0; int all_mask, all_revs; enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER; - char head[128]; - const char *head_p; - int head_len; + char *head; struct object_id head_oid; int merge_base = 0; int independent = 0; @@ -787,32 +780,24 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) snarf_refs(all_heads, all_remotes); } - head_p = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - head_oid.hash, NULL); - if (head_p) { - head_len = strlen(head_p); - memcpy(head, head_p, head_len + 1); - } - else { - head_len = 0; - head[0] = 0; - } + head = resolve_refdup("HEAD", RESOLVE_REF_READING, + head_oid.hash, NULL); - if (with_current_branch && head_p) { + if (with_current_branch && head) { int has_head = 0; for (i = 0; !has_head && i < ref_name_cnt; i++) { /* We are only interested in adding the branch * HEAD points at. */ if (rev_is_head(head, - head_len, ref_name[i], head_oid.hash, NULL)) has_head++; } if (!has_head) { - int offset = starts_with(head, "refs/heads/") ? 11 : 0; - append_one_rev(head + offset); + const char *name = head; + skip_prefix(name, "refs/heads/", &name); + append_one_rev(name); } } @@ -866,7 +851,6 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) for (i = 0; i < num_rev; i++) { int j; int is_head = rev_is_head(head, - head_len, ref_name[i], head_oid.hash, rev[i]->object.oid.hash); @@ -1045,9 +1045,6 @@ static inline int is_empty_tree_oid(const struct object_id *oid) return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN); } - -int git_mkstemp(char *path, size_t n, const char *template); - /* set default permissions by passing mode arguments to open(2) */ int git_mkstemps_mode(char *pattern, int suffix_len, int mode); int git_mkstemp_mode(char *pattern, int mode); @@ -1109,7 +1106,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path, int die_on_error); const char *real_path(const char *path); const char *real_path_if_valid(const char *path); -char *real_pathdup(const char *path); +char *real_pathdup(const char *path, int die_on_error); const char *absolute_path(const char *path); char *absolute_pathdup(const char *path); const char *remove_leading_path(const char *in, const char *prefix); @@ -1319,7 +1316,37 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */ -extern int interpret_branch_name(const char *str, int len, struct strbuf *); +/* + * This reads short-hand syntax that not only evaluates to a commit + * object name, but also can act as if the end user spelled the name + * of the branch from the command line. + * + * - "@{-N}" finds the name of the Nth previous branch we were on, and + * places the name of the branch in the given buf and returns the + * number of characters parsed if successful. + * + * - "<branch>@{upstream}" finds the name of the other ref that + * <branch> is configured to merge with (missing <branch> defaults + * to the current branch), and places the name of the branch in the + * given buf and returns the number of characters parsed if + * successful. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + * + * If the input was ok but there are not N branch switches in the + * reflog, it returns 0. + * + * If "allowed" is non-zero, it is a treated as a bitfield of allowable + * expansions: local branches ("refs/heads/"), remote branches + * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is + * allowed, even ones to refs outside of those namespaces. + */ +#define INTERPRET_BRANCH_LOCAL (1<<0) +#define INTERPRET_BRANCH_REMOTE (1<<1) +#define INTERPRET_BRANCH_HEAD (1<<2) +extern int interpret_branch_name(const char *str, int len, struct strbuf *, + unsigned allowed); extern int get_oid_mb(const char *str, struct object_id *oid); extern int validate_headref(const char *ref); @@ -1819,8 +1846,11 @@ extern int git_config_include(const char *name, const char *value, void *data); * * (i.e., what gets handed to a config_fn_t). The caller provides the section; * we return -1 if it does not match, 0 otherwise. The subsection and key - * out-parameters are filled by the function (and subsection is NULL if it is + * out-parameters are filled by the function (and *subsection is NULL if it is * missing). + * + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if + * there is no subsection at all. */ extern int parse_config_key(const char *var, const char *section, diff --git a/ci/run-linux32-build.sh b/ci/run-linux32-build.sh new file mode 100755 index 0000000000..e30fb2cddc --- /dev/null +++ b/ci/run-linux32-build.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Build and test Git in a 32-bit environment +# +# Usage: +# run-linux32-build.sh [host-user-id] +# + +# Update packages to the latest available versions +linux32 --32bit i386 sh -c ' + apt update >/dev/null && + apt install -y build-essential libcurl4-openssl-dev libssl-dev \ + libexpat-dev gettext python >/dev/null +' && + +# If this script runs inside a docker container, then all commands are +# usually executed as root. Consequently, the host user might not be +# able to access the test output files. +# If a host user id is given, then create a user "ci" with the host user +# id to make everything accessible to the host user. +HOST_UID=$1 && +CI_USER=$USER && +test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) && + +# Build and test +linux32 --32bit i386 su -m -l $CI_USER -c ' + cd /usr/src/git && + make --jobs=2 && + make --quiet test +' @@ -1308,11 +1308,11 @@ void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data) static inline int standard_header_field(const char *field, size_t len) { - return ((len == 4 && !memcmp(field, "tree ", 5)) || - (len == 6 && !memcmp(field, "parent ", 7)) || - (len == 6 && !memcmp(field, "author ", 7)) || - (len == 9 && !memcmp(field, "committer ", 10)) || - (len == 8 && !memcmp(field, "encoding ", 9))); + return ((len == 4 && !memcmp(field, "tree", 4)) || + (len == 6 && !memcmp(field, "parent", 6)) || + (len == 6 && !memcmp(field, "author", 6)) || + (len == 9 && !memcmp(field, "committer", 9)) || + (len == 8 && !memcmp(field, "encoding", 8))); } static int excluded_header_field(const char *field, size_t len, const char **exclude) @@ -1322,8 +1322,7 @@ static int excluded_header_field(const char *field, size_t len, const char **exc while (*exclude) { size_t xlen = strlen(*exclude); - if (len == xlen && - !memcmp(field, *exclude, xlen) && field[xlen] == ' ') + if (len == xlen && !memcmp(field, *exclude, xlen)) return 1; exclude++; } @@ -1354,12 +1353,11 @@ static struct commit_extra_header *read_commit_extra_header_lines( strbuf_reset(&buf); it = NULL; - eof = strchr(line, ' '); - if (next <= eof) + eof = memchr(line, ' ', next - line); + if (!eof) eof = next; - - if (standard_header_field(line, eof - line) || - excluded_header_field(line, eof - line, exclude)) + else if (standard_header_field(line, eof - line) || + excluded_header_field(line, eof - line, exclude)) continue; it = xcalloc(1, sizeof(*it)); @@ -201,11 +201,105 @@ void git_config_push_parameter(const char *text) strbuf_release(&env); } +static inline int iskeychar(int c) +{ + return isalnum(c) || c == '-'; +} + +/* + * Auxiliary function to sanity-check and split the key into the section + * identifier and variable name. + * + * Returns 0 on success, -1 when there is an invalid character in the key and + * -2 if there is no section name in the key. + * + * store_key - pointer to char* which will hold a copy of the key with + * lowercase section and variable name + * baselen - pointer to int which will hold the length of the + * section + subsection part, can be NULL + */ +static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) +{ + int i, dot, baselen; + const char *last_dot = strrchr(key, '.'); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + + if (last_dot == NULL || last_dot == key) { + if (!quiet) + error("key does not contain a section: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + if (!last_dot[1]) { + if (!quiet) + error("key does not contain variable name: %s", key); + return -CONFIG_NO_SECTION_OR_NAME; + } + + baselen = last_dot - key; + if (baselen_) + *baselen_ = baselen; + + /* + * Validate the key and while at it, lower case it for matching. + */ + if (store_key) + *store_key = xmallocz(strlen(key)); + + dot = 0; + for (i = 0; key[i]; i++) { + unsigned char c = key[i]; + if (c == '.') + dot = 1; + /* Leave the extended basename untouched.. */ + if (!dot || i > baselen) { + if (!iskeychar(c) || + (i == baselen + 1 && !isalpha(c))) { + if (!quiet) + error("invalid key: %s", key); + goto out_free_ret_1; + } + c = tolower(c); + } else if (c == '\n') { + if (!quiet) + error("invalid key (newline): %s", key); + goto out_free_ret_1; + } + if (store_key) + (*store_key)[i] = c; + } + + return 0; + +out_free_ret_1: + if (store_key) { + free(*store_key); + *store_key = NULL; + } + return -CONFIG_INVALID_KEY; +} + +int git_config_parse_key(const char *key, char **store_key, int *baselen) +{ + return git_config_parse_key_1(key, store_key, baselen, 0); +} + +int git_config_key_is_valid(const char *key) +{ + return !git_config_parse_key_1(key, NULL, NULL, 1); +} + int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { const char *value; + char *canonical_name; struct strbuf **pair; + int ret; pair = strbuf_split_str(text, '=', 2); if (!pair[0]) @@ -223,13 +317,15 @@ int git_config_parse_parameter(const char *text, strbuf_list_free(pair); return error("bogus config parameter: %s", text); } - strbuf_tolower(pair[0]); - if (fn(pair[0]->buf, value, data) < 0) { - strbuf_list_free(pair); - return -1; + + if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) { + ret = -1; + } else { + ret = (fn(canonical_name, value, data) < 0) ? -1 : 0; + free(canonical_name); } strbuf_list_free(pair); - return 0; + return ret; } int git_config_from_parameters(config_fn_t fn, void *data) @@ -356,11 +452,6 @@ static char *parse_value(void) } } -static inline int iskeychar(int c) -{ - return isalnum(c) || c == '-'; -} - static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; @@ -1990,93 +2081,6 @@ void git_config_set(const char *key, const char *value) } /* - * Auxiliary function to sanity-check and split the key into the section - * identifier and variable name. - * - * Returns 0 on success, -1 when there is an invalid character in the key and - * -2 if there is no section name in the key. - * - * store_key - pointer to char* which will hold a copy of the key with - * lowercase section and variable name - * baselen - pointer to int which will hold the length of the - * section + subsection part, can be NULL - */ -static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet) -{ - int i, dot, baselen; - const char *last_dot = strrchr(key, '.'); - - /* - * Since "key" actually contains the section name and the real - * key name separated by a dot, we have to know where the dot is. - */ - - if (last_dot == NULL || last_dot == key) { - if (!quiet) - error("key does not contain a section: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - if (!last_dot[1]) { - if (!quiet) - error("key does not contain variable name: %s", key); - return -CONFIG_NO_SECTION_OR_NAME; - } - - baselen = last_dot - key; - if (baselen_) - *baselen_ = baselen; - - /* - * Validate the key and while at it, lower case it for matching. - */ - if (store_key) - *store_key = xmallocz(strlen(key)); - - dot = 0; - for (i = 0; key[i]; i++) { - unsigned char c = key[i]; - if (c == '.') - dot = 1; - /* Leave the extended basename untouched.. */ - if (!dot || i > baselen) { - if (!iskeychar(c) || - (i == baselen + 1 && !isalpha(c))) { - if (!quiet) - error("invalid key: %s", key); - goto out_free_ret_1; - } - c = tolower(c); - } else if (c == '\n') { - if (!quiet) - error("invalid key (newline): %s", key); - goto out_free_ret_1; - } - if (store_key) - (*store_key)[i] = c; - } - - return 0; - -out_free_ret_1: - if (store_key) { - free(*store_key); - *store_key = NULL; - } - return -CONFIG_INVALID_KEY; -} - -int git_config_parse_key(const char *key, char **store_key, int *baselen) -{ - return git_config_parse_key_1(key, store_key, baselen, 0); -} - -int git_config_key_is_valid(const char *key) -{ - return !git_config_parse_key_1(key, NULL, NULL, 1); -} - -/* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. * if value_regex==CONFIG_REGEX_NONE, do not match any existing values @@ -2536,11 +2540,10 @@ int parse_config_key(const char *var, const char **subsection, int *subsection_len, const char **key) { - int section_len = strlen(section); const char *dot; /* Does it start with "section." ? */ - if (!starts_with(var, section) || var[section_len] != '.') + if (!skip_prefix(var, section, &var) || *var != '.') return -1; /* @@ -2552,12 +2555,16 @@ int parse_config_key(const char *var, *key = dot + 1; /* Did we have a subsection at all? */ - if (dot == var + section_len) { - *subsection = NULL; - *subsection_len = 0; + if (dot == var) { + if (subsection) { + *subsection = NULL; + *subsection_len = 0; + } } else { - *subsection = var + section_len + 1; + if (!subsection) + return -1; + *subsection = var + 1; *subsection_len = dot - *subsection; } diff --git a/config.mak.uname b/config.mak.uname index 447f36ac2e..399fe19271 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -27,7 +27,6 @@ endif ifeq ($(uname_S),Linux) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease HAVE_DEV_TTY = YesPlease @@ -41,7 +40,6 @@ endif ifeq ($(uname_S),GNU/kFreeBSD) HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease @@ -55,7 +53,6 @@ ifeq ($(uname_S),UnixWare) SHELL_PATH = /usr/local/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -Kthread BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -79,7 +76,6 @@ ifeq ($(uname_S),SCO_SV) SHELL_PATH = /usr/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease - NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib NO_STRCASESTR = YesPlease @@ -122,7 +118,6 @@ ifeq ($(uname_S),SunOS) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_REGEX = YesPlease NO_MSGFMT_EXTENDED_OPTIONS = YesPlease HAVE_DEV_TTY = YesPlease @@ -168,7 +163,6 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease OLD_ICONV = UnfortunatelyYes @@ -233,7 +227,6 @@ ifeq ($(uname_S),NetBSD) BASIC_CFLAGS += -I/usr/pkg/include BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib USE_ST_TIMESPEC = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease HAVE_BSD_SYSCTL = YesPlease endif @@ -242,7 +235,6 @@ ifeq ($(uname_S),AIX) NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes @@ -263,7 +255,6 @@ ifeq ($(uname_S),GNU) # GNU/Hurd HAVE_ALLOCA_H = YesPlease NO_STRLCPY = YesPlease - NO_MKSTEMPS = YesPlease HAVE_PATHS_H = YesPlease LIBC_CONTAINS_LIBINTL = YesPlease endif @@ -272,7 +263,6 @@ ifeq ($(uname_S),IRIX) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -291,7 +281,6 @@ ifeq ($(uname_S),IRIX64) NO_UNSETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_MKDTEMP = YesPlease # When compiled with the MIPSpro 7.4.4m compiler, and without pthreads # (i.e. NO_PTHREADS is set), and _with_ MMAP (i.e. NO_MMAP is not set), @@ -311,7 +300,6 @@ ifeq ($(uname_S),HP-UX) NO_SETENV = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_MKDTEMP = YesPlease NO_UNSETENV = YesPlease @@ -352,7 +340,6 @@ ifeq ($(uname_S),Windows) NO_ICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease RUNTIME_PREFIX = YesPlease @@ -402,7 +389,6 @@ ifeq ($(uname_S),Interix) NO_MKDTEMP = YesPlease NO_STRTOUMAX = YesPlease NO_NSEC = YesPlease - NO_MKSTEMPS = YesPlease ifeq ($(uname_R),3.5) NO_INET_NTOP = YesPlease NO_INET_PTON = YesPlease @@ -461,7 +447,6 @@ ifeq ($(uname_S),NONSTOP_KERNEL) NO_SETENV = YesPlease NO_UNSETENV = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease # Currently libiconv-1.9.1. OLD_ICONV = UnfortunatelyYes NO_REGEX = YesPlease @@ -503,7 +488,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) NEEDS_LIBICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease RUNTIME_PREFIX = YesPlease @@ -515,7 +499,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo NO_REGEX = YesPlease NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease ETAGS_TARGET = ETAGS NO_INET_PTON = YesPlease NO_INET_NTOP = YesPlease @@ -584,7 +567,6 @@ ifeq ($(uname_S),QNX) NO_ICONV = YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease - NO_MKSTEMPS = YesPlease NO_NSEC = YesPlease NO_PTHREADS = YesPlease NO_R_TO_GCC_LINKER = YesPlease diff --git a/configure.ac b/configure.ac index 0b15f04b10..128165529f 100644 --- a/configure.ac +++ b/configure.ac @@ -1050,12 +1050,6 @@ GIT_CHECK_FUNC(mkdtemp, [NO_MKDTEMP=YesPlease]) GIT_CONF_SUBST([NO_MKDTEMP]) # -# Define NO_MKSTEMPS if you don't have mkstemps in the C library. -GIT_CHECK_FUNC(mkstemps, -[NO_MKSTEMPS=], -[NO_MKSTEMPS=YesPlease]) -GIT_CONF_SUBST([NO_MKSTEMPS]) -# # Define NO_INITGROUPS if you don't have initgroups in the C library. GIT_CHECK_FUNC(initgroups, [NO_INITGROUPS=], diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci index 2d7f25d99f..4ba98b7eaf 100644 --- a/contrib/coccinelle/array.cocci +++ b/contrib/coccinelle/array.cocci @@ -24,3 +24,19 @@ expression n; @@ - memcpy(dst, src, n * sizeof(T)); + COPY_ARRAY(dst, src, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(*ptr)); ++ ALLOC_ARRAY(ptr, n); + +@@ +type T; +T *ptr; +expression n; +@@ +- ptr = xmalloc(n * sizeof(T)); ++ ALLOC_ARRAY(ptr, n); diff --git a/contrib/coccinelle/strbuf.cocci b/contrib/coccinelle/strbuf.cocci index 63995f22ff..1d580e49b0 100644 --- a/contrib/coccinelle/strbuf.cocci +++ b/contrib/coccinelle/strbuf.cocci @@ -38,3 +38,9 @@ expression E1, E2, E3; @@ - strbuf_addstr(E1, find_unique_abbrev(E2, E3)); + strbuf_add_unique_abbrev(E1, E2, E3); + +@@ +expression E1, E2; +@@ +- strbuf_addstr(E1, real_path(E2)); ++ strbuf_add_real_path(E1, E2); diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr index 712a1377e2..1c3d87f861 100755 --- a/contrib/remote-helpers/git-remote-bzr +++ b/contrib/remote-helpers/git-remote-bzr @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-bzr is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-bzr\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-bzr is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-bzr +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-bzr \\ +WARNING: $ wget -O $HOME/bin/git-remote-bzr \ WARNING: https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr WARNING: $ chmod +x $HOME/bin/git-remote-bzr -''') +EOT diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 4255ad6312..8e9188364c 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -1,13 +1,11 @@ -#!/usr/bin/env python +#!/bin/sh -import sys - -sys.stderr.write('WARNING: git-remote-hg is now maintained independently.\n') -sys.stderr.write('WARNING: For more information visit https://github.com/felipec/git-remote-hg\n') - -sys.stderr.write('''WARNING: +cat >&2 <<'EOT' +WARNING: git-remote-hg is now maintained independently. +WARNING: For more information visit https://github.com/felipec/git-remote-hg +WARNING: WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-hg \\ +WARNING: $ wget -O $HOME/bin/git-remote-hg \ WARNING: https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg WARNING: $ chmod +x $HOME/bin/git-remote-hg -''') +EOT @@ -2870,8 +2870,25 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) s->should_free = 1; return 0; } - if (size_only) + + /* + * Even if the caller would be happy with getting + * only the size, we cannot return early at this + * point if the path requires us to run the content + * conversion. + */ + if (size_only && !would_convert_to_git(s->path)) return 0; + + /* + * Note: this check uses xsize_t(st.st_size) that may + * not be the true size of the blob after it goes + * through convert_to_git(). This may not strictly be + * correct, but the whole point of big_file_threshold + * and is_binary check being that we want to avoid + * opening the file and inspecting the contents, this + * is probably fine. + */ if ((flags & CHECK_BINARY) && s->size > big_file_threshold && s->is_binary == -1) { s->is_binary = 1; diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c index 9795ca1c15..341529b5a8 100644 --- a/diffcore-pickaxe.c +++ b/diffcore-pickaxe.c @@ -81,12 +81,15 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws) regmatch_t regmatch; int flags = 0; - while (*data && + while (sz && *data && !regexec_buf(regexp, data, sz, 1, ®match, flags)) { flags |= REG_NOTBOL; data += regmatch.rm_eo; - if (*data && regmatch.rm_so == regmatch.rm_eo) + sz -= regmatch.rm_eo; + if (sz && *data && regmatch.rm_so == regmatch.rm_eo) { data++; + sz--; + } cnt++; } @@ -2730,8 +2730,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_) { struct strbuf file_name = STRBUF_INIT; struct strbuf rel_path = STRBUF_INIT; - char *git_dir = real_pathdup(git_dir_); - char *work_tree = real_pathdup(work_tree_); + char *git_dir = real_pathdup(git_dir_, 1); + char *work_tree = real_pathdup(work_tree_, 1); /* Update gitfile */ strbuf_addf(&file_name, "%s/.git", work_tree); diff --git a/environment.c b/environment.c index c07fb17fb7..42dc3106d2 100644 --- a/environment.c +++ b/environment.c @@ -259,7 +259,7 @@ void set_git_work_tree(const char *new_work_tree) return; } git_work_tree_initialized = 1; - work_tree = real_pathdup(new_work_tree); + work_tree = real_pathdup(new_work_tree, 1); } const char *get_git_work_tree(void) diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c index 61f6a43579..f73210973f 100644 --- a/ewah/ewah_io.c +++ b/ewah/ewah_io.c @@ -142,8 +142,8 @@ int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len) * the endianness conversion in a separate pass to ensure * we're loading 8-byte aligned words. */ - memcpy(self->buffer, ptr, self->buffer_size * sizeof(uint64_t)); - ptr += self->buffer_size * sizeof(uint64_t); + memcpy(self->buffer, ptr, self->buffer_size * sizeof(eword_t)); + ptr += self->buffer_size * sizeof(eword_t); for (i = 0; i < self->buffer_size; ++i) self->buffer[i] = ntohll(self->buffer[i]); diff --git a/fetch-pack.c b/fetch-pack.c index 601f0779a1..f12bfcdbb1 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -578,7 +578,7 @@ static void filter_refs(struct fetch_pack_args *args, break; /* definitely do not have it */ else if (cmp == 0) { keep = 1; /* definitely have it */ - sought[i]->matched = 1; + sought[i]->match_status = REF_MATCHED; } i++; } @@ -598,22 +598,24 @@ static void filter_refs(struct fetch_pack_args *args, } /* Append unmatched requests to the list */ - if ((allow_unadvertised_object_request & - (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) { - for (i = 0; i < nr_sought; i++) { - unsigned char sha1[20]; + for (i = 0; i < nr_sought; i++) { + unsigned char sha1[20]; - ref = sought[i]; - if (ref->matched) - continue; - if (get_sha1_hex(ref->name, sha1) || - ref->name[40] != '\0' || - hashcmp(sha1, ref->old_oid.hash)) - continue; + ref = sought[i]; + if (ref->match_status != REF_NOT_MATCHED) + continue; + if (get_sha1_hex(ref->name, sha1) || + ref->name[40] != '\0' || + hashcmp(sha1, ref->old_oid.hash)) + continue; - ref->matched = 1; + if ((allow_unadvertised_object_request & + (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) { + ref->match_status = REF_MATCHED; *newtail = copy_ref(ref); newtail = &(*newtail)->next; + } else { + ref->match_status = REF_UNADVERTISED_NOT_ALLOWED; } } *refs = newlist; @@ -1094,3 +1096,26 @@ struct ref *fetch_pack(struct fetch_pack_args *args, clear_shallow_info(&si); return ref_cpy; } + +int report_unmatched_refs(struct ref **sought, int nr_sought) +{ + int i, ret = 0; + + for (i = 0; i < nr_sought; i++) { + if (!sought[i]) + continue; + switch (sought[i]->match_status) { + case REF_MATCHED: + continue; + case REF_NOT_MATCHED: + error(_("no such remote ref %s"), sought[i]->name); + break; + case REF_UNADVERTISED_NOT_ALLOWED: + error(_("Server does not allow request for unadvertised object %s"), + sought[i]->name); + break; + } + ret = 1; + } + return ret; +} diff --git a/fetch-pack.h b/fetch-pack.h index c912e3d321..a2d46e6e75 100644 --- a/fetch-pack.h +++ b/fetch-pack.h @@ -45,4 +45,10 @@ struct ref *fetch_pack(struct fetch_pack_args *args, struct sha1_array *shallow, char **pack_lockfile); +/* + * Print an appropriate error message for each sought ref that wasn't + * matched. Return 0 if all sought refs were matched, otherwise 1. + */ +int report_unmatched_refs(struct ref **sought, int nr_sought); + #endif diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 982593c89e..f5c816e273 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -92,7 +92,7 @@ sub colored { } # command line options -my $cmd; +my $patch_mode_only; my $patch_mode; my $patch_mode_revision; @@ -1299,7 +1299,7 @@ sub patch_update_cmd { } return 0; } - if ($patch_mode) { + if ($patch_mode_only) { @them = @mods; } else { @@ -1721,7 +1721,7 @@ sub process_args { die sprintf(__("invalid argument %s, expecting --"), $arg) unless $arg eq "--"; %patch_mode_flavour = %{$patch_modes{$patch_mode}}; - $cmd = 1; + $patch_mode_only = 1; } elsif ($arg ne "--") { die sprintf(__("invalid argument %s, expecting --"), $arg); @@ -1758,7 +1758,7 @@ sub main_loop { process_args(); refresh(); -if ($cmd) { +if ($patch_mode_only) { patch_update_cmd(); } else { diff --git a/git-compat-util.h b/git-compat-util.h index ef6d560e15..e626851fe9 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -639,11 +639,6 @@ extern int gitsetenv(const char *, const char *, int); extern char *gitmkdtemp(char *); #endif -#ifdef NO_MKSTEMPS -#define mkstemps gitmkstemps -extern int gitmkstemps(char *, int); -#endif - #ifdef NO_UNSETENV #define unsetenv gitunsetenv extern void gitunsetenv(const char *); diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 86b2ff1e07..2b8cdba157 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -46,6 +46,8 @@ git_commit_non_empty_tree() { if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then map "$3" + elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then + : else git commit-tree "$@" fi diff --git a/git-send-email.perl b/git-send-email.perl index 068d60b3e6..eea0a517f7 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1563,7 +1563,7 @@ foreach my $t (@files) { # Now parse the message body while(<$fh>) { $message .= $_; - if (/^(Signed-off-by|Cc): (.*)$/i) { + if (/^(Signed-off-by|Cc): ([^>]*>?)/i) { chomp; my ($what, $c) = ($1, $2); chomp $c; diff --git a/http-walker.c b/http-walker.c index b34b6ace7c..ee049cb13d 100644 --- a/http-walker.c +++ b/http-walker.c @@ -168,6 +168,11 @@ static int is_alternate_allowed(const char *url) }; int i; + if (http_follow_config != HTTP_FOLLOW_ALWAYS) { + warning("alternate disabled by http.followRedirects: %s", url); + return 0; + } + for (i = 0; i < ARRAY_SIZE(protocols); i++) { const char *end; if (skip_prefix(url, protocols[i], &end) && @@ -296,13 +301,16 @@ static void process_alternates_response(void *callback_data) okay = 1; } } - /* skip "objects\n" at end */ if (okay) { struct strbuf target = STRBUF_INIT; strbuf_add(&target, base, serverlen); - strbuf_add(&target, data + i, posn - i - 7); - - if (is_alternate_allowed(target.buf)) { + strbuf_add(&target, data + i, posn - i); + if (!strbuf_strip_suffix(&target, "objects")) { + warning("ignoring alternate that does" + " not end in 'objects': %s", + target.buf); + strbuf_release(&target); + } else if (is_alternate_allowed(target.buf)) { warning("adding alternate object store: %s", target.buf); newalt = xmalloc(sizeof(*newalt)); @@ -314,6 +322,8 @@ static void process_alternates_response(void *callback_data) while (tail->next != NULL) tail = tail->next; tail->next = newalt; + } else { + strbuf_release(&target); } } } @@ -331,9 +341,6 @@ static void fetch_alternates(struct walker *walker, const char *base) struct alternates_request alt_req; struct walker_data *cdata = walker->data; - if (http_follow_config != HTTP_FOLLOW_ALWAYS) - return; - /* * If another request has already started fetching alternates, * wait for them to arrive and return to processing this request's @@ -109,7 +109,7 @@ static int curl_save_cookies; struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; static const char *user_agent; -static int curl_empty_auth; +static int curl_empty_auth = -1; enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL; @@ -125,6 +125,14 @@ static struct credential cert_auth = CREDENTIAL_INIT; static int ssl_cert_password_required; #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY static unsigned long http_auth_methods = CURLAUTH_ANY; +static int http_auth_methods_restricted; +/* Modes for which empty_auth cannot actually help us. */ +static unsigned long empty_auth_useless = + CURLAUTH_BASIC +#ifdef CURLAUTH_DIGEST_IE + | CURLAUTH_DIGEST_IE +#endif + | CURLAUTH_DIGEST; #endif static struct curl_slist *pragma_header; @@ -333,7 +341,10 @@ static int http_options(const char *var, const char *value, void *cb) return git_config_string(&user_agent, var, value); if (!strcmp("http.emptyauth", var)) { - curl_empty_auth = git_config_bool(var, value); + if (value && !strcmp("auto", value)) + curl_empty_auth = -1; + else + curl_empty_auth = git_config_bool(var, value); return 0; } @@ -382,10 +393,37 @@ static int http_options(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static int curl_empty_auth_enabled(void) +{ + if (curl_empty_auth >= 0) + return curl_empty_auth; + +#ifndef LIBCURL_CAN_HANDLE_AUTH_ANY + /* + * Our libcurl is too old to do AUTH_ANY in the first place; + * just default to turning the feature off. + */ +#else + /* + * In the automatic case, kick in the empty-auth + * hack as long as we would potentially try some + * method more exotic than "Basic" or "Digest". + * + * But only do this when this is our second or + * subsequent request, as by then we know what + * methods are available. + */ + if (http_auth_methods_restricted && + (http_auth_methods & ~empty_auth_useless)) + return 1; +#endif + return 0; +} + static void init_curl_http_auth(CURL *result) { if (!http_auth.username || !*http_auth.username) { - if (curl_empty_auth) + if (curl_empty_auth_enabled()) curl_easy_setopt(result, CURLOPT_USERPWD, ":"); return; } @@ -1079,7 +1117,7 @@ struct active_request_slot *get_active_slot(void) #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods); #endif - if (http_auth.password || curl_empty_auth) + if (http_auth.password || curl_empty_auth_enabled()) init_curl_http_auth(slot->curl); return slot; @@ -1347,6 +1385,10 @@ static int handle_curl_result(struct slot_results *results) } else { #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE; + if (results->auth_avail) { + http_auth_methods &= results->auth_avail; + http_auth_methods_restricted = 1; + } #endif return HTTP_REAUTH; } @@ -1727,6 +1769,9 @@ static int http_request_reauth(const char *url, { int ret = http_request(url, result, target, options); + if (ret != HTTP_OK && ret != HTTP_REAUTH) + return ret; + if (options && options->effective_url && options->base_url) { if (update_url_from_redirect(options->base_url, url, options->effective_url)) { @@ -153,7 +153,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email, const char *ident_default_name(void) { - if (!git_default_name.len) { + if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) { copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name); strbuf_trim(&git_default_name); } @@ -162,7 +162,7 @@ const char *ident_default_name(void) const char *ident_default_email(void) { - if (!git_default_email.len) { + if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) { const char *email = getenv("EMAIL"); if (email && email[0]) { @@ -203,6 +203,15 @@ static int crud(unsigned char c) c == '\''; } +static int has_non_crud(const char *str) +{ + for (; *str; str++) { + if (!crud(*str)) + return 1; + } + return 0; +} + /* * Copy over a string to the destination, but avoid special * characters ('\n', '<' and '>') and remove crud at the end @@ -351,19 +360,32 @@ const char *fmt_ident(const char *name, const char *email, int want_date = !(flag & IDENT_NO_DATE); int want_name = !(flag & IDENT_NO_NAME); + if (!email) { + if (strict && ident_use_config_only + && !(ident_config_given & IDENT_MAIL_GIVEN)) { + fputs(_(env_hint), stderr); + die(_("no email was given and auto-detection is disabled")); + } + email = ident_default_email(); + if (strict && default_email_is_bogus) { + fputs(_(env_hint), stderr); + die(_("unable to auto-detect email address (got '%s')"), email); + } + } + if (want_name) { int using_default = 0; if (!name) { if (strict && ident_use_config_only && !(ident_config_given & IDENT_NAME_GIVEN)) { fputs(_(env_hint), stderr); - die("no name was given and auto-detection is disabled"); + die(_("no name was given and auto-detection is disabled")); } name = ident_default_name(); using_default = 1; if (strict && default_name_is_bogus) { fputs(_(env_hint), stderr); - die("unable to auto-detect name (got '%s')", name); + die(_("unable to auto-detect name (got '%s')"), name); } } if (!*name) { @@ -371,24 +393,13 @@ const char *fmt_ident(const char *name, const char *email, if (strict) { if (using_default) fputs(_(env_hint), stderr); - die("empty ident name (for <%s>) not allowed", email); + die(_("empty ident name (for <%s>) not allowed"), email); } pw = xgetpwuid_self(NULL); name = pw->pw_name; } - } - - if (!email) { - if (strict && ident_use_config_only - && !(ident_config_given & IDENT_MAIL_GIVEN)) { - fputs(_(env_hint), stderr); - die("no email was given and auto-detection is disabled"); - } - email = ident_default_email(); - if (strict && default_email_is_bogus) { - fputs(_(env_hint), stderr); - die("unable to auto-detect email address (got '%s')", email); - } + if (strict && !has_non_crud(name)) + die(_("name consists only of disallowed characters: %s"), name); } strbuf_reset(&ident); @@ -403,7 +414,7 @@ const char *fmt_ident(const char *name, const char *email, strbuf_addch(&ident, ' '); if (date_str && date_str[0]) { if (parse_date(date_str, &ident) < 0) - die("invalid date format: %s", date_str); + die(_("invalid date format: %s"), date_str); } else strbuf_addstr(&ident, ident_default_date()); diff --git a/line-log.c b/line-log.c index 65f3558b3b..a23b910471 100644 --- a/line-log.c +++ b/line-log.c @@ -43,9 +43,10 @@ void range_set_release(struct range_set *rs) static void range_set_copy(struct range_set *dst, struct range_set *src) { range_set_init(dst, src->nr); - memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set)); + COPY_ARRAY(dst->ranges, src->ranges, src->nr); dst->nr = src->nr; } + static void range_set_move(struct range_set *dst, struct range_set *src) { range_set_release(dst); @@ -144,7 +145,7 @@ void sort_and_merge_range_set(struct range_set *rs) static void range_set_union(struct range_set *out, struct range_set *a, struct range_set *b) { - int i = 0, j = 0, o = 0; + int i = 0, j = 0; struct range *ra = a->ranges; struct range *rb = b->ranges; /* cannot make an alias of out->ranges: it may change during grow */ @@ -167,16 +168,15 @@ static void range_set_union(struct range_set *out, new = &rb[j++]; if (new->start == new->end) ; /* empty range */ - else if (!o || out->ranges[o-1].end < new->start) { + else if (!out->nr || out->ranges[out->nr-1].end < new->start) { range_set_grow(out, 1); - out->ranges[o].start = new->start; - out->ranges[o].end = new->end; - o++; - } else if (out->ranges[o-1].end < new->end) { - out->ranges[o-1].end = new->end; + out->ranges[out->nr].start = new->start; + out->ranges[out->nr].end = new->end; + out->nr++; + } else if (out->ranges[out->nr-1].end < new->end) { + out->ranges[out->nr-1].end = new->end; } } - out->nr = o; } /* diff --git a/pack-objects.h b/pack-objects.h index cc9b9a9b90..03f1191659 100644 --- a/pack-objects.h +++ b/pack-objects.h @@ -30,12 +30,16 @@ struct object_entry { /* * State flags for depth-first search used for analyzing delta cycles. + * + * The depth is measured in delta-links to the base (so if A is a delta + * against B, then A has a depth of 1, and B a depth of 0). */ enum { DFS_NONE = 0, DFS_ACTIVE, DFS_DONE } dfs_state; + int depth; }; struct packing_data { diff --git a/progress.c b/progress.c index 76a88c573f..29378caa05 100644 --- a/progress.c +++ b/progress.c @@ -243,21 +243,18 @@ void stop_progress_msg(struct progress **p_progress, const char *msg) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ - char buf[128], *bufp; - size_t len = strlen(msg) + 5; + char *buf; struct throughput *tp = progress->throughput; - bufp = (len < sizeof(buf)) ? buf : xmallocz(len); if (tp) { unsigned int rate = !tp->avg_misecs ? 0 : tp->avg_bytes / tp->avg_misecs; throughput_string(&tp->display, tp->curr_total, rate); } progress_update = 1; - xsnprintf(bufp, len + 1, ", %s.\n", msg); - display(progress, progress->last_value, bufp); - if (buf != bufp) - free(bufp); + buf = xstrfmt(", %s.\n", msg); + display(progress, progress->last_value, buf); + free(buf); } clear_progress_signal(); if (progress->throughput) @@ -404,7 +404,7 @@ int refname_match(const char *abbrev_name, const char *full_name) static char *substitute_branch_name(const char **string, int *len) { struct strbuf buf = STRBUF_INIT; - int ret = interpret_branch_name(*string, *len, &buf); + int ret = interpret_branch_name(*string, *len, &buf, 0); if (ret == *len) { size_t size; @@ -1034,10 +1034,10 @@ static struct string_list *hide_refs; int parse_hide_refs_config(const char *var, const char *value, const char *section) { + const char *key; if (!strcmp("transfer.hiderefs", var) || - /* NEEDSWORK: use parse_config_key() once both are merged */ - (starts_with(var, section) && var[strlen(section)] == '.' && - !strcmp(var + strlen(section), ".hiderefs"))) { + (!parse_config_key(var, section, NULL, NULL, &key) && + !strcmp(key, "hiderefs"))) { char *ref; int len; @@ -693,7 +693,7 @@ static struct remote *remote_get_1(const char *name, name = get_default(current_branch, &name_given); ret = make_remote(name, 0); - if (valid_remote_nick(name)) { + if (valid_remote_nick(name) && have_git_dir()) { if (!valid_remote(ret)) read_remotes_file(ret); if (!valid_remote(ret)) @@ -89,8 +89,13 @@ struct ref { force:1, forced_update:1, expect_old_sha1:1, - deletion:1, - matched:1; + deletion:1; + + enum { + REF_NOT_MATCHED = 0, /* initial value */ + REF_MATCHED, + REF_UNADVERTISED_NOT_ALLOWED + } match_status; /* * Order is important here, as we write to FETCH_HEAD diff --git a/revision.c b/revision.c index b37dbec378..771d079f6e 100644 --- a/revision.c +++ b/revision.c @@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs, revs->no_walk = 0; if (revs->reflog_info && obj->type == OBJ_COMMIT) { struct strbuf buf = STRBUF_INIT; - int len = interpret_branch_name(name, 0, &buf); + int len = interpret_branch_name(name, 0, &buf, 0); int st; if (0 < len && name[len] && buf.len) diff --git a/run-command.c b/run-command.c index 5227f78aea..574b81d3e8 100644 --- a/run-command.c +++ b/run-command.c @@ -48,7 +48,7 @@ static void cleanup_children(int sig, int in_signal) kill(p->pid, sig); - if (p->process->wait_after_clean) { + if (p->process && p->process->wait_after_clean) { p->next = children_to_wait_for; children_to_wait_for = p; } else { diff --git a/send-pack.c b/send-pack.c index 6195b43e9a..d2d2a49a02 100644 --- a/send-pack.c +++ b/send-pack.c @@ -72,6 +72,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru struct child_process po = CHILD_PROCESS_INIT; FILE *po_in; int i; + int rc; i = 4; if (args->use_thin_pack) @@ -125,27 +126,44 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru po.out = -1; } - if (finish_command(&po)) + rc = finish_command(&po); + if (rc) { + /* + * For a normal non-zero exit, we assume pack-objects wrote + * something useful to stderr. For death by signal, though, + * we should mention it to the user. The exception is SIGPIPE + * (141), because that's a normal occurence if the remote end + * hangs up (and we'll report that by trying to read the unpack + * status). + */ + if (rc > 128 && rc != 141) + error("pack-objects died of signal %d", rc - 128); return -1; + } + return 0; +} + +static int receive_unpack_status(int in) +{ + const char *line = packet_read_line(in, NULL); + if (!skip_prefix(line, "unpack ", &line)) + return error(_("unable to parse remote unpack status: %s"), line); + if (strcmp(line, "ok")) + return error(_("remote unpack failed: %s"), line); return 0; } static int receive_status(int in, struct ref *refs) { struct ref *hint; - int ret = 0; - char *line = packet_read_line(in, NULL); - if (!starts_with(line, "unpack ")) - return error("did not receive remote status"); - if (strcmp(line, "unpack ok")) { - error("unpack failed: %s", line + 7); - ret = -1; - } + int ret; + hint = NULL; + ret = receive_unpack_status(in); while (1) { char *refname; char *msg; - line = packet_read_line(in, NULL); + char *line = packet_read_line(in, NULL); if (!line) break; if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { @@ -557,6 +575,14 @@ int send_pack(struct send_pack_args *args, close(out); if (git_connection_is_socket(conn)) shutdown(fd[0], SHUT_WR); + + /* + * Do not even bother with the return value; we know we + * are failing, and just want the error() side effects. + */ + if (status_report) + receive_unpack_status(in); + if (use_sideband) { close(demux.out); finish_async(&demux); @@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) if (!is_absolute_path(data.buf)) strbuf_addf(&path, "%s/", gitdir); strbuf_addbuf(&path, &data); - strbuf_addstr(sb, real_path(path.buf)); + strbuf_add_real_path(sb, path.buf); ret = 1; } else { strbuf_addstr(sb, gitdir); @@ -698,7 +698,7 @@ static const char *setup_discovered_git_dir(const char *gitdir, /* --work-tree is set without --git-dir; use discovered one */ if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) { if (offset != cwd->len && !is_absolute_path(gitdir)) - gitdir = real_pathdup(gitdir); + gitdir = real_pathdup(gitdir, 1); if (chdir(cwd->buf)) die_errno("Could not come back to cwd"); return setup_explicit_git_dir(gitdir, cwd, nongit_ok); @@ -806,7 +806,7 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, /* Keep entry but do not canonicalize it */ return 1; } else { - char *real_path = real_pathdup(ceil); + char *real_path = real_pathdup(ceil, 0); if (!real_path) { return 0; } diff --git a/sha1_file.c b/sha1_file.c index ec957db5e1..8ce80d4481 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2532,6 +2532,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, while (delta_stack_nr) { void *delta_data; void *base = data; + void *external_base = NULL; unsigned long delta_size, base_size = size; int i; @@ -2558,6 +2559,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, p->pack_name); mark_bad_packed_object(p, base_sha1); base = read_object(base_sha1, &type, &base_size); + external_base = base; } } @@ -2576,6 +2578,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, "at offset %"PRIuMAX" from %s", (uintmax_t)curpos, p->pack_name); data = NULL; + free(external_base); continue; } @@ -2595,6 +2598,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset, error("failed to apply delta"); free(delta_data); + free(external_base); } *final_type = type; diff --git a/sha1_name.c b/sha1_name.c index 73a915ff1b..26ceec1d79 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1176,7 +1176,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str return 1; } -static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf) +static int reinterpret(const char *name, int namelen, int len, + struct strbuf *buf, unsigned allowed) { /* we have extra data, which might need further processing */ struct strbuf tmp = STRBUF_INIT; @@ -1184,7 +1185,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu int ret; strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, buf->len, &tmp); + ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); /* that data was not interpreted, remove our cruft */ if (ret < 0) { strbuf_setlen(buf, used); @@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref) free(s); } +static int branch_interpret_allowed(const char *refname, unsigned allowed) +{ + if (!allowed) + return 1; + + if ((allowed & INTERPRET_BRANCH_LOCAL) && + starts_with(refname, "refs/heads/")) + return 1; + if ((allowed & INTERPRET_BRANCH_REMOTE) && + starts_with(refname, "refs/remotes/")) + return 1; + + return 0; +} + static int interpret_branch_mark(const char *name, int namelen, int at, struct strbuf *buf, int (*get_mark)(const char *, int), const char *(*get_data)(struct branch *, - struct strbuf *)) + struct strbuf *), + unsigned allowed) { int len; struct branch *branch; @@ -1234,64 +1251,55 @@ static int interpret_branch_mark(const char *name, int namelen, if (!value) die("%s", err.buf); + if (!branch_interpret_allowed(value, allowed)) + return -1; + set_shortened_ref(buf, value); return len + at; } -/* - * This reads short-hand syntax that not only evaluates to a commit - * object name, but also can act as if the end user spelled the name - * of the branch from the command line. - * - * - "@{-N}" finds the name of the Nth previous branch we were on, and - * places the name of the branch in the given buf and returns the - * number of characters parsed if successful. - * - * - "<branch>@{upstream}" finds the name of the other ref that - * <branch> is configured to merge with (missing <branch> defaults - * to the current branch), and places the name of the branch in the - * given buf and returns the number of characters parsed if - * successful. - * - * If the input is not of the accepted format, it returns a negative - * number to signal an error. - * - * If the input was ok but there are not N branch switches in the - * reflog, it returns 0. - */ -int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, + unsigned allowed) { char *at; const char *start; - int len = interpret_nth_prior_checkout(name, namelen, buf); + int len; if (!namelen) namelen = strlen(name); - if (!len) { - return len; /* syntax Ok, not enough switches */ - } else if (len > 0) { - if (len == namelen) - return len; /* consumed all */ - else - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { + len = interpret_nth_prior_checkout(name, namelen, buf); + if (!len) { + return len; /* syntax Ok, not enough switches */ + } else if (len > 0) { + if (len == namelen) + return len; /* consumed all */ + else + return reinterpret(name, namelen, len, buf, allowed); + } } for (start = name; (at = memchr(start, '@', namelen - (start - name))); start = at + 1) { - len = interpret_empty_at(name, namelen, at - name, buf); - if (len > 0) - return reinterpret(name, namelen, len, buf); + if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { + len = interpret_empty_at(name, namelen, at - name, buf); + if (len > 0) + return reinterpret(name, namelen, len, buf, + allowed); + } len = interpret_branch_mark(name, namelen, at - name, buf, - upstream_mark, branch_get_upstream); + upstream_mark, branch_get_upstream, + allowed); if (len > 0) return len; len = interpret_branch_mark(name, namelen, at - name, buf, - push_mark, branch_get_push); + push_mark, branch_get_push, + allowed); if (len > 0) return len; } @@ -1299,22 +1307,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf) return -1; } -int strbuf_branchname(struct strbuf *sb, const char *name) +void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed) { int len = strlen(name); - int used = interpret_branch_name(name, len, sb); + int used = interpret_branch_name(name, len, sb, allowed); - if (used == len) - return 0; if (used < 0) used = 0; strbuf_add(sb, name + used, len - used); - return len; } int strbuf_check_branch_ref(struct strbuf *sb, const char *name) { - strbuf_branchname(sb, name); + strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL); if (name[0] == '-') return -1; strbuf_splice(sb, 0, 0, "refs/heads/", 11); @@ -707,6 +707,17 @@ void strbuf_add_absolute_path(struct strbuf *sb, const char *path) strbuf_addstr(sb, path); } +void strbuf_add_real_path(struct strbuf *sb, const char *path) +{ + if (sb->len) { + struct strbuf resolved = STRBUF_INIT; + strbuf_realpath(&resolved, path, 1); + strbuf_addbuf(sb, &resolved); + strbuf_release(&resolved); + } else + strbuf_realpath(sb, path, 1); +} + int printf_ln(const char *fmt, ...) { int ret; @@ -441,6 +441,20 @@ extern int strbuf_getcwd(struct strbuf *sb); */ extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path); +/** + * Canonize `path` (make it absolute, resolve symlinks, remove extra + * slashes) and append it to `sb`. Die with an informative error + * message if there is a problem. + * + * The directory part of `path` (i.e., everything up to the last + * dir_sep) must denote a valid, existing directory, but the last + * component need not exist. + * + * Callers that don't mind links should use the more lightweight + * strbuf_add_absolute_path() instead. + */ +extern void strbuf_add_real_path(struct strbuf *sb, const char *path); + /** * Normalize in-place the path contained in the strbuf. See @@ -560,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb) strbuf_complete(sb, '\n'); } -extern int strbuf_branchname(struct strbuf *sb, const char *name); +/* + * Copy "name" to "sb", expanding any special @-marks as handled by + * interpret_branch_name(). The result is a non-qualified branch name + * (so "foo" or "origin/master" instead of "refs/heads/foo" or + * "refs/remotes/origin/master"). + * + * Note that the resulting name may not be a syntactically valid refname. + * + * If "allowed" is non-zero, restrict the set of allowed expansions. See + * interpret_branch_name() for details. + */ +extern void strbuf_branchname(struct strbuf *sb, const char *name, + unsigned allowed); + +/* + * Like strbuf_branchname() above, but confirm that the result is + * syntactically valid to be used as a local branch name in refs/heads/. + * + * The return value is "0" if the result is valid, and "-1" otherwise. + */ extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); extern void strbuf_addstr_urlencode(struct strbuf *, const char *, diff --git a/submodule-config.c b/submodule-config.c index 93453909cf..bb069bc097 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -333,7 +333,7 @@ static int parse_config(const char *var, const char *value, void *data) strcmp(value, "all") && strcmp(value, "none")) warning("Invalid parameter '%s' for config option " - "'submodule.%s.ignore'", value, var); + "'submodule.%s.ignore'", value, name.buf); else { free((void *) submodule->ignore); submodule->ignore = xstrdup(value); diff --git a/submodule.c b/submodule.c index 3b98766a6b..0a2831d846 100644 --- a/submodule.c +++ b/submodule.c @@ -1403,7 +1403,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, /* If it is an actual gitfile, it doesn't need migration. */ return; - real_old_git_dir = real_pathdup(old_git_dir); + real_old_git_dir = real_pathdup(old_git_dir, 1); sub = submodule_from_path(null_sha1, path); if (!sub) @@ -1412,7 +1412,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); if (!prefix) prefix = get_super_prefix(); @@ -1472,14 +1472,14 @@ void absorb_git_dir_into_superproject(const char *prefix, new_git_dir = git_path("modules/%s", sub->name); if (safe_create_leading_directories_const(new_git_dir) < 0) die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir); + real_new_git_dir = real_pathdup(new_git_dir, 1); connect_work_tree_and_git_dir(path, real_new_git_dir); free(real_new_git_dir); } else { /* Is it already absorbed into the superprojects git dir? */ - char *real_sub_git_dir = real_pathdup(sub_git_dir); - char *real_common_git_dir = real_pathdup(get_git_common_dir()); + char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); + char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1); if (!starts_with(real_sub_git_dir, real_common_git_dir)) relocate_single_git_dir_into_superproject(prefix, path); diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index d5dab5a94f..006d2a8152 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -110,7 +110,12 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || { } perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || { - skip_all='skipping gitweb tests, CGI module unusable' + skip_all='skipping gitweb tests, CGI & CGI::Util & CGI::Carp modules not available' + test_done +} + +perl -mTime::HiRes -e 0 >/dev/null 2>&1 || { + skip_all='skipping gitweb tests, Time::HiRes module not available' test_done } diff --git a/t/interop/.gitignore b/t/interop/.gitignore new file mode 100644 index 0000000000..49c78d3dba --- /dev/null +++ b/t/interop/.gitignore @@ -0,0 +1,4 @@ +/trash directory*/ +/test-results/ +/.prove/ +/build/ diff --git a/t/interop/Makefile b/t/interop/Makefile new file mode 100644 index 0000000000..31a4bbc716 --- /dev/null +++ b/t/interop/Makefile @@ -0,0 +1,16 @@ +-include ../../config.mak +export GIT_TEST_OPTIONS + +SHELL_PATH ?= $(SHELL) +SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) +T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh)) + +all: $(T) + +$(T): + @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) + +clean: + rm -rf build "trash directory".* test-results + +.PHONY: all clean $(T) diff --git a/t/interop/README b/t/interop/README new file mode 100644 index 0000000000..72d42bd856 --- /dev/null +++ b/t/interop/README @@ -0,0 +1,85 @@ +Git version interoperability tests +================================== + +This directory has interoperability tests for git. Each script is +similar to the normal test scripts found in t/, but with the added twist +that two special versions of git, "git.a" and "git.b", are available in +the PATH. Individual tests can then check the interaction between the +two versions. + +When you add a feature that handles backwards compatibility between git +versions, it's encouraged to add a test here to make sure it behaves as +you expect. + + +Running Tests +------------- + +The easiest way to run tests is to say "make". This runs all +the tests against their default versions. + +You can run a single test like: + + $ ./i0000-basic.sh + ok 1 - bare git is forbidden + ok 2 - git.a version (v1.6.6.3) + ok 3 - git.b version (v2.11.1) + # passed all 3 test(s) + 1..3 + +Each test contains default versions to run against. You may override +these by setting `GIT_TEST_VERSION_A` and `GIT_TEST_VERSION_B` in the +environment. Note that not all combinations will give sensible outcomes +for all tests (e.g., a test checking for a specific old/new interaction +may want something "old" enough" and something "new" enough; see +individual tests for details). + +Version names should be resolvable as revisions in the current +repository. They will be exported and built as needed using the +config.mak files found at the root of your working tree. + +The exception is the special version "." which uses the currently-built +contents of your working tree. + +You can set the following variables (in the environment or in your config.mak): + + GIT_INTEROP_MAKE_OPTS + Options to pass to `make` when building a git version (e.g., + `-j8`). + +You can also pass any command-line options taken by ordinary git tests (e.g., +"-v"). + + +Naming Tests +------------ + +The interop test files are named like: + + iNNNN-short-description.sh + +where N is a decimal digit. The same conventions for choosing NNNN as +for normal tests apply. + + +Writing Tests +------------- + +An interop test script starts like a normal script, declaring a few +variables and then including interop-lib.sh (which includes test-lib.sh). +Besides test_description, you should also set the $VERSION_A and $VERSION_B +variables to give the default versions to test against. See t0000-basic.sh for +an example. + +You can then use test_expect_success as usual, with a few differences: + + 1. The special commands "git.a" and "git.b" correspond to the + two versions. + + 2. You cannot call a bare "git". This is to prevent accidents where + you meant "git.a" or "git.b". + + 3. The trash directory is _not_ a git repository by default. You + should create one with the appropriate version of git. + +At the end of the script, call test_done as usual. diff --git a/t/interop/i0000-basic.sh b/t/interop/i0000-basic.sh new file mode 100755 index 0000000000..903e9193f8 --- /dev/null +++ b/t/interop/i0000-basic.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Note that this test only works on real version numbers, +# as it depends on matching the output to "git version". +VERSION_A=v1.6.6.3 +VERSION_B=v2.11.1 + +test_description='sanity test interop library' +. ./interop-lib.sh + +test_expect_success 'bare git is forbidden' ' + test_must_fail git version +' + +test_expect_success "git.a version ($VERSION_A)" ' + echo git version ${VERSION_A#v} >expect && + git.a version >actual && + test_cmp expect actual +' + +test_expect_success "git.b version ($VERSION_B)" ' + echo git version ${VERSION_B#v} >expect && + git.b version >actual && + test_cmp expect actual +' + +test_done diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh new file mode 100755 index 0000000000..1daf69420b --- /dev/null +++ b/t/interop/i5500-git-daemon.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +VERSION_A=. +VERSION_B=v1.0.0 + +: ${LIB_GIT_DAEMON_PORT:=5500} +LIB_GIT_DAEMON_COMMAND='git.a daemon' + +test_description='clone and fetch by older client' +. ./interop-lib.sh +. "$TEST_DIRECTORY"/lib-git-daemon.sh + +start_git_daemon --export-all + +repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo + +test_expect_success "create repo served by $VERSION_A" ' + git.a init "$repo" && + git.a -C "$repo" commit --allow-empty -m one +' + +test_expect_success "clone with $VERSION_B" ' + git.b clone "$GIT_DAEMON_URL/repo" child && + echo one >expect && + git.a -C child log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success "fetch with $VERSION_B" ' + git.a -C "$repo" commit --allow-empty -m two && + ( + cd child && + git.b fetch + ) && + echo two >expect && + git.a -C child log -1 --format=%s FETCH_HEAD >actual && + test_cmp expect actual +' + +stop_git_daemon +test_done diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh new file mode 100644 index 0000000000..3e0a2911d4 --- /dev/null +++ b/t/interop/interop-lib.sh @@ -0,0 +1,92 @@ +# Interoperability testing framework. Each script should source +# this after setting default $VERSION_A and $VERSION_B variables. + +. ../../GIT-BUILD-OPTIONS +INTEROP_ROOT=$(pwd) +BUILD_ROOT=$INTEROP_ROOT/build + +build_version () { + if test -z "$1" + then + echo >&2 "error: test script did not set default versions" + return 1 + fi + + if test "$1" = "." + then + git rev-parse --show-toplevel + return 0 + fi + + sha1=$(git rev-parse "$1^{tree}") || return 1 + dir=$BUILD_ROOT/$sha1 + + if test -e "$dir/.built" + then + echo "$dir" + return 0 + fi + + echo >&2 "==> Building $1..." + + mkdir -p "$dir" || return 1 + + (cd "$(git rev-parse --show-cdup)" && git archive --format=tar "$sha1") | + (cd "$dir" && tar x) || + return 1 + + for config in config.mak config.mak.autogen config.status + do + if test -e "$INTEROP_ROOT/../../$config" + then + cp "$INTEROP_ROOT/../../$config" "$dir/" || return 1 + fi + done + + ( + cd "$dir" && + make $GIT_INTEROP_MAKE_OPTS >&2 && + touch .built + ) || return 1 + + echo "$dir" +} + +# Old versions of git don't have bin-wrappers, so let's give a rough emulation. +wrap_git () { + write_script "$1" <<-EOF + GIT_EXEC_PATH="$2" + export GIT_EXEC_PATH + PATH="$2:\$PATH" + export GIT_EXEC_PATH + exec git "\$@" + EOF +} + +generate_wrappers () { + mkdir -p .bin && + wrap_git .bin/git.a "$DIR_A" && + wrap_git .bin/git.b "$DIR_B" && + write_script .bin/git <<-\EOF && + echo >&2 fatal: test tried to run generic git + exit 1 + EOF + PATH=$(pwd)/.bin:$PATH +} + +VERSION_A=${GIT_TEST_VERSION_A:-$VERSION_A} +VERSION_B=${GIT_TEST_VERSION_B:-$VERSION_B} + +if ! DIR_A=$(build_version "$VERSION_A") || + ! DIR_B=$(build_version "$VERSION_B") +then + echo >&2 "fatal: unable to build git versions" + exit 1 +fi + +TEST_DIRECTORY=$INTEROP_ROOT/.. +TEST_OUTPUT_DIRECTORY=$INTEROP_ROOT +TEST_NO_CREATE_REPO=t +. "$TEST_DIRECTORY"/test-lib.sh + +generate_wrappers || die "unable to set up interop test environment" diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh index f9cbd47931..987d40680b 100644 --- a/t/lib-git-daemon.sh +++ b/t/lib-git-daemon.sh @@ -46,7 +46,8 @@ start_git_daemon() { say >&3 "Starting git daemon ..." mkfifo git_daemon_output - git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ + ${LIB_GIT_DAEMON_COMMAND:-git daemon} \ + --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ --reuseaddr --verbose \ --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 69174c6e31..0642ae7e6e 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -133,6 +133,15 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302] RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302] +# redir-to/502/x?y -> really-redir-to?path=502/x&qs=y which returns 502 +# redir-to/x?y -> really-redir-to?path=x&qs=y -> x?y +RewriteCond %{QUERY_STRING} ^(.*)$ +RewriteRule ^/redir-to/(.*)$ /really-redir-to?path=$1&qs=%1 [R=302] +RewriteCond %{QUERY_STRING} ^path=502/(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ - [R=502,L] +RewriteCond %{QUERY_STRING} ^path=(.*)&qs=(.*)$ +RewriteRule ^/really-redir-to$ /%1?%2 [R=302] + # The first rule issues a client-side redirect to something # that _doesn't_ look like a git repo. The second rule is a # server-side rewrite, so that it turns out the odd-looking diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh index 16359d51ae..ebf172401b 100755 --- a/t/perf/p0001-rev-list.sh +++ b/t/perf/p0001-rev-list.sh @@ -15,7 +15,8 @@ test_perf 'rev-list --all --objects' ' ' test_expect_success 'create new unreferenced commit' ' - commit=$(git commit-tree HEAD^{tree} -p HEAD) + commit=$(git commit-tree HEAD^{tree} -p HEAD) && + test_export commit ' test_perf 'rev-list $commit --not --all' ' diff --git a/t/perf/p7000-filter-branch.sh b/t/perf/p7000-filter-branch.sh index 15ee5d1d53..b029586ccb 100755 --- a/t/perf/p7000-filter-branch.sh +++ b/t/perf/p7000-filter-branch.sh @@ -16,4 +16,9 @@ test_perf 'noop filter' ' git filter-branch -f base..HEAD ' +test_perf 'noop prune-empty' ' + git checkout --detach tip && + git filter-branch -f --prune-empty base..HEAD +' + test_done diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index 46f08ee087..ab4b8b06ae 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -83,7 +83,7 @@ test_perf_create_repo_from () { error "bug in the test script: not 2 parameters to test-create-repo" repo="$1" source="$2" - source_git="$(git -C "$source" rev-parse --git-dir)" + source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)" objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)" mkdir -p "$repo/.git" ( @@ -102,7 +102,7 @@ test_perf_create_repo_from () { ) && ( cd "$repo" && - git init -q && { + "$MODERN_GIT" init -q && { test_have_prereq SYMLINKS || git config core.symlinks false } && diff --git a/t/perf/run b/t/perf/run index e8adedadfd..c788d713ae 100755 --- a/t/perf/run +++ b/t/perf/run @@ -63,6 +63,9 @@ run_dirs_helper () { unset GIT_TEST_INSTALLED else GIT_TEST_INSTALLED="$mydir/bin-wrappers" + # Older versions of git lacked bin-wrappers; fallback to the + # files in the root. + test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir export GIT_TEST_INSTALLED fi run_one_dir "$@" diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh index e0a6940232..58c0b7e9b6 100755 --- a/t/t0100-previous.sh +++ b/t/t0100-previous.sh @@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' ' test_must_fail git merge @{-100} ' +test_expect_success 'log -g @{-1}' ' + git checkout -b last_branch && + git checkout -b new_branch && + echo "last_branch@{0}" >expect && + git log -g --format=%gd @{-1} >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 923bfc5a26..ea371020fa 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1097,6 +1097,68 @@ test_expect_success 'multiple git -c appends config' ' test_cmp expect actual ' +test_expect_success 'last one wins: two level vars' ' + + # sec.var and sec.VAR are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + + git -c sec.var=val -c sec.VAR=VAL config --get sec.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.var >actual && + test_cmp expect actual && + + git -c sec.var=val -c sec.VAR=VAL config --get SEC.var >actual && + test_cmp expect actual && + git -c SEC.var=val -c sec.var=VAL config --get sec.VAR >actual && + test_cmp expect actual +' + +test_expect_success 'last one wins: three level vars' ' + + # v.a.r and v.A.r are not the same variable, as the middle + # level of a three-level configuration variable name is + # case sensitive. + + echo val >expect && + git -c v.a.r=val -c v.A.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.A.r=VAL config --get V.a.R >actual && + test_cmp expect actual && + + # v.a.r and V.a.R are the same variable, as the first + # and the last level of a configuration variable name is + # case insensitive. + + echo VAL >expect && + git -c v.a.r=val -c v.a.R=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get v.a.r >actual && + test_cmp expect actual && + git -c v.a.r=val -c v.a.R=VAL config --get V.a.R >actual && + test_cmp expect actual && + git -c v.a.r=val -c V.a.r=VAL config --get V.a.R >actual && + test_cmp expect actual +' + +for VAR in a .a a. a.0b a."b c". a."b c".0d +do + test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" ' + test_must_fail git -c "$VAR=VAL" config -l + ' +done + +for VAR in a.b a."b c".d +do + test_expect_success "git -c $VAR=VAL works with valid '$VAR'" ' + echo VAL >expect && + git -c "$VAR=VAL" config --get "$VAR" >actual && + test_cmp expect actual + ' +done + test_expect_success 'git -c is not confused by empty environment' ' GIT_CONFIG_PARAMETERS="" git -c x.one=1 config --list ' diff --git a/t/t1501-work-tree.sh b/t/t1501-work-tree.sh index cc5b870e58..b06210ec5e 100755 --- a/t/t1501-work-tree.sh +++ b/t/t1501-work-tree.sh @@ -423,4 +423,12 @@ test_expect_success '$GIT_WORK_TREE overrides $GIT_DIR/common' ' ) ' +test_expect_success 'error out gracefully on invalid $GIT_WORK_TREE' ' + ( + GIT_WORK_TREE=/.invalid/work/tree && + export GIT_WORK_TREE && + test_expect_code 128 git rev-parse + ) +' + test_done diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh new file mode 100755 index 0000000000..698d9cc4f3 --- /dev/null +++ b/t/t3204-branch-name-interpretation.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +test_description='interpreting exotic branch name arguments + +Branch name arguments are usually names which are taken to be inside of +refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc. +This script aims to check the behavior of those corner cases. +' +. ./test-lib.sh + +expect_branch() { + git log -1 --format=%s "$1" >actual && + echo "$2" >expect && + test_cmp expect actual +} + +expect_deleted() { + test_must_fail git rev-parse --verify "$1" +} + +test_expect_success 'set up repo' ' + test_commit one && + test_commit two && + git remote add origin foo.git +' + +test_expect_success 'update branch via @{-1}' ' + git branch previous one && + + git checkout previous && + git checkout master && + + git branch -f @{-1} two && + expect_branch previous two +' + +test_expect_success 'update branch via local @{upstream}' ' + git branch local one && + git branch --set-upstream-to=local && + + git branch -f @{upstream} two && + expect_branch local two +' + +test_expect_success 'disallow updating branch via remote @{upstream}' ' + git update-ref refs/remotes/origin/remote one && + git branch --set-upstream-to=origin/remote && + + test_must_fail git branch -f @{upstream} two +' + +test_expect_success 'create branch with pseudo-qualified name' ' + git branch refs/heads/qualified two && + expect_branch refs/heads/refs/heads/qualified two +' + +test_expect_success 'delete branch via @{-1}' ' + git branch previous-del && + + git checkout previous-del && + git checkout master && + + git branch -D @{-1} && + expect_deleted previous-del +' + +test_expect_success 'delete branch via local @{upstream}' ' + git branch local-del && + git branch --set-upstream-to=local-del && + + git branch -D @{upstream} && + expect_deleted local-del +' + +test_expect_success 'delete branch via remote @{upstream}' ' + git update-ref refs/remotes/origin/remote-del two && + git branch --set-upstream-to=origin/remote-del && + + git branch -r -D @{upstream} && + expect_deleted origin/remote-del +' + +# Note that we create two oddly named local branches here. We want to make +# sure that we do not accidentally delete either of them, even if +# shorten_unambiguous_ref() tweaks the name to avoid ambiguity. +test_expect_success 'delete @{upstream} expansion matches -r option' ' + git update-ref refs/remotes/origin/remote-del two && + git branch --set-upstream-to=origin/remote-del && + git update-ref refs/heads/origin/remote-del two && + git update-ref refs/heads/remotes/origin/remote-del two && + + test_must_fail git branch -D @{upstream} && + expect_branch refs/heads/origin/remote-del two && + expect_branch refs/heads/remotes/origin/remote-del two +' + +test_expect_success 'disallow deleting remote branch via @{-1}' ' + git update-ref refs/remotes/origin/previous one && + + git checkout -b origin/previous two && + git checkout master && + + test_must_fail git branch -r -D @{-1} && + expect_branch refs/remotes/origin/previous one && + expect_branch refs/heads/origin/previous two +' + +# The thing we are testing here is that "@" is the real branch refs/heads/@, +# and not refs/heads/HEAD. These tests should not imply that refs/heads/@ is a +# sane thing, but it _is_ technically allowed for now. If we disallow it, these +# can be switched to test_must_fail. +test_expect_success 'create branch named "@"' ' + git branch -f @ one && + expect_branch refs/heads/@ one +' + +test_expect_success 'delete branch named "@"' ' + git update-ref refs/heads/@ two && + git branch -D @ && + expect_deleted refs/heads/@ +' + +test_expect_success 'checkout does not treat remote @{upstream} as a branch' ' + git update-ref refs/remotes/origin/checkout one && + git branch --set-upstream-to=origin/checkout && + git update-ref refs/heads/origin/checkout two && + git update-ref refs/heads/remotes/origin/checkout two && + + git checkout @{upstream} && + expect_branch HEAD one +' + +test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5ffe78e920..aaa258daa3 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -394,4 +394,22 @@ test_expect_success 'diffs can be colorized' ' grep "$(printf "\\033")" output ' +test_expect_success 'patch-mode via -i prompts for files' ' + git reset --hard && + + echo one >file && + echo two >test && + git add -i <<-\EOF && + patch + test + + y + quit + EOF + + echo test >expect && + git diff --cached --name-only >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index 461f4bb583..2f1737fcef 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -152,4 +152,13 @@ test_expect_success 'git diff --quiet ignores stat-change only entries' ' test_expect_code 1 git diff --quiet ' +test_expect_success 'git diff --quiet on a path that need conversion' ' + echo "crlf.txt text=auto" >.gitattributes && + printf "Hello\r\nWorld\r\n" >crlf.txt && + git add .gitattributes crlf.txt && + + printf "Hello\r\nWorld\n" >crlf.txt && + git diff --quiet crlf.txt +' + test_done diff --git a/t/t4062-diff-pickaxe.sh b/t/t4062-diff-pickaxe.sh index f0bf50bda7..7c4903f497 100755 --- a/t/t4062-diff-pickaxe.sh +++ b/t/t4062-diff-pickaxe.sh @@ -19,4 +19,9 @@ test_expect_success '-G matches' ' test 4096-zeroes.txt = "$(cat out)" ' +test_expect_success '-S --pickaxe-regex' ' + git diff --name-only -S0 --pickaxe-regex HEAD^ >out && + verbose test 4096-zeroes.txt = "$(cat out)" +' + test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 9d87777b59..d0377fae5c 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -106,4 +106,14 @@ test_expect_success '-L with --output' ' test_line_count = 70 log ' +test_expect_success 'range_set_union' ' + test_seq 500 > c.c && + git add c.c && + git commit -m "many lines" && + test_seq 1000 > c.c && + git add c.c && + git commit -m "modify many lines" && + git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done) +' + test_done diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh new file mode 100755 index 0000000000..37143ea0ac --- /dev/null +++ b/t/t5316-pack-delta-depth.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +test_description='pack-objects breaks long cross-pack delta chains' +. ./test-lib.sh + +# This mirrors a repeated push setup: +# +# 1. A client repeatedly modifies some files, makes a +# commit, and pushes the result. It does this N times +# before we get around to repacking. +# +# 2. Each push generates a thin pack with the new version of +# various objects. Let's consider some file in the root tree +# which is updated in each commit. +# +# When generating push number X, we feed commit X-1 (and +# thus blob X-1) as a preferred base. The resulting pack has +# blob X as a thin delta against blob X-1. +# +# On the receiving end, "index-pack --fix-thin" will +# complete the pack with a base copy of blob X-1. +# +# 3. In older versions of git, if we used the delta from +# pack X, then we'd always find blob X-1 as a base in the +# same pack (and generate a fresh delta). +# +# But with the pack mru, we jump from delta to delta +# following the traversal order: +# +# a. We grab blob X from pack X as a delta, putting it at +# the tip of our mru list. +# +# b. Eventually we move onto commit X-1. We need other +# objects which are only in pack X-1 (in the test code +# below, it's the containing tree). That puts pack X-1 +# at the tip of our mru list. +# +# c. Eventually we look for blob X-1, and we find the +# version in pack X-1 (because it's the mru tip). +# +# Now we have blob X as a delta against X-1, which is a delta +# against X-2, and so forth. +# +# In the real world, these small pushes would get exploded by +# unpack-objects rather than "index-pack --fix-thin", but the +# same principle applies to larger pushes (they only need one +# repeatedly-modified file to generate the delta chain). + +test_expect_success 'create series of packs' ' + test-genrandom foo 4096 >content && + prev= && + for i in $(test_seq 1 10) + do + cat content >file && + echo $i >>file && + git add file && + git commit -m $i && + cur=$(git rev-parse HEAD^{tree}) && + { + test -n "$prev" && echo "-$prev" + echo $cur + echo "$(git rev-parse :file) file" + } | git pack-objects --stdout >tmp && + git index-pack --stdin --fix-thin <tmp || return 1 + prev=$cur + done +' + +max_chain() { + git index-pack --verify-stat-only "$1" >output && + perl -lne ' + /chain length = (\d+)/ and $len = $1; + END { print $len } + ' output +} + +# Note that this whole setup is pretty reliant on the current +# packing heuristics. We double-check that our test case +# actually produces a long chain. If it doesn't, it should be +# adjusted (or scrapped if the heuristics have become too unreliable) +test_expect_success 'packing produces a long delta' ' + # Use --window=0 to make sure we are seeing reused deltas, + # not computing a new long chain. + pack=$(git pack-objects --all --window=0 </dev/null pack) && + test 9 = "$(max_chain pack-$pack.pack)" +' + +test_expect_success '--depth limits depth' ' + pack=$(git pack-objects --all --depth=5 </dev/null pack) && + test 5 = "$(max_chain pack-$pack.pack)" +' + +test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 505e1b4a7f..b5865b385d 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -484,7 +484,7 @@ test_expect_success 'test lonely missing ref' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy ) >/dev/null 2>error-m && - test_cmp expect-error error-m + test_i18ncmp expect-error error-m ' test_expect_success 'test missing ref after existing' ' @@ -492,7 +492,7 @@ test_expect_success 'test missing ref after existing' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy ) >/dev/null 2>error-em && - test_cmp expect-error error-em + test_i18ncmp expect-error error-em ' test_expect_success 'test missing ref before existing' ' @@ -500,7 +500,7 @@ test_expect_success 'test missing ref before existing' ' cd client && test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A ) >/dev/null 2>error-me && - test_cmp expect-error error-me + test_i18ncmp expect-error error-me ' test_expect_success 'test --all, --depth, and explicit head' ' diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index ba46e86de0..3ea27107c2 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -153,6 +153,25 @@ test_expect_success 'remove errors out early when deleting non-existent branch' ) ' +test_expect_success 'remove remote with a branch without configured merge' ' + test_when_finished "( + git -C test checkout master; + git -C test branch -D two; + git -C test config --remove-section remote.two; + git -C test config --remove-section branch.second; + true + )" && + ( + cd test && + git remote add two ../two && + git fetch two && + git checkout -b second two/master^0 && + git config branch.second.remote two && + git checkout master && + git remote rm two + ) +' + test_expect_success 'rename errors out early when deleting non-existent branch' ' ( cd test && diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 55fc83fc06..94fc9be9ce 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -248,4 +248,13 @@ test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-complian test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git ' +test_expect_success 'ls-remote works outside repository' ' + # It is important for this repo to be inside the nongit + # area, as we want a repo name that does not include + # slashes (because those inhibit some of our configuration + # lookups). + nongit git init --bare dst.git && + nongit git ls-remote dst.git +' + test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 0fc5a7c596..177897ea0b 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1098,7 +1098,8 @@ test_expect_success 'fetch exact SHA1' ' test_must_fail git cat-file -t $the_commit && # fetching the hidden object should fail by default - test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy && + test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err && + test_i18ngrep "Server does not allow request for unadvertised object" err && test_must_fail git rev-parse --verify refs/heads/copy && # the server side can allow it to succeed diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index aeb3a63f7c..87308cdced 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -34,6 +34,15 @@ test_expect_success 'clone http repository' ' test_cmp file clone/file ' +test_expect_success 'list refs from outside any repository' ' + cat >expect <<-EOF && + $(git rev-parse master) HEAD + $(git rev-parse master) refs/heads/master + EOF + nongit git ls-remote "$HTTPD_URL/dumb/repo.git" >actual && + test_cmp expect actual +' + test_expect_success 'create password-protected repository' ' mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" && cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ @@ -378,5 +387,14 @@ test_expect_success 'http-alternates triggers not-from-user protocol check' ' clone $HTTPD_URL/dumb/evil.git evil-user ' +test_expect_success 'can redirect through non-"info/refs?service=git-upload-pack" URL' ' + git clone "$HTTPD_URL/redir-to/dumb/repo.git" +' + +test_expect_success 'print HTTP error when any intermediate redirect throws error' ' + test_must_fail git clone "$HTTPD_URL/redir-to/502" 2> stderr && + test_i18ngrep "unable to access.*/redir-to/502" stderr +' + stop_httpd test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index aea1dfc714..a468041c50 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -558,7 +558,7 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' test_when_finished "git checkout master" && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && sed -e "s/^\* / /" actual >expect && - git checkout --orphan HEAD && + git checkout --orphan orphaned-branch && git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && test_cmp expect actual ' diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 1762dfa6a3..08de2e8ab0 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -67,5 +67,20 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre test_line_count = 2 new # There is one new pack and its .idx ' +test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' ' + test_commit foo && + test_commit bar && + git repack && + test_config gc.autopacklimit 1 && + test_config gc.autodetach true && + echo fleem >.git/gc.log && + test_must_fail git gc --auto 2>err && + test_i18ngrep "^error:" err && + test_config gc.logexpiry 5.days && + test-chmtime =-345600 .git/gc.log && + test_must_fail git gc --auto && + test_config gc.logexpiry 2.days && + git gc --auto +' test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index cb8fbd8e5e..7cb60799be 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -313,6 +313,27 @@ test_expect_success 'Tag name filtering allows slashes in tag names' ' git cat-file tag X/2 > actual && test_cmp expect actual ' +test_expect_success 'setup --prune-empty comparisons' ' + git checkout --orphan master-no-a && + git rm -rf . && + unset test_tick && + test_tick && + GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && + test_commit --notick B B.t B Bx && + git checkout -b branch-no-a Bx && + test_commit D D.t D Dx && + mkdir dir && + test_commit dir/D dir/D.t dir/D dir/Dx && + test_commit E E.t E Ex && + git checkout master-no-a && + test_commit C C.t C Cx && + git checkout branch-no-a && + git merge Cx -m "Merge tag '\''C'\'' into branch" && + git tag Fx && + test_commit G G.t G Gx && + test_commit H H.t H Hx && + git checkout branch +' test_expect_success 'Prune empty commits' ' git rev-list HEAD > expect && @@ -341,6 +362,22 @@ test_expect_success 'prune empty works even without index/tree filters' ' test_cmp expect actual ' +test_expect_success '--prune-empty is able to prune root commit' ' + git rev-list branch-no-a >expect && + git branch testing H && + git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing && + git rev-list testing >actual && + git branch -D testing && + test_cmp expect actual +' + +test_expect_success '--prune-empty is able to prune entire branch' ' + git branch prune-entire B && + git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire && + test_path_is_missing .git/refs/heads/prune-entire && + test_must_fail git reflog exists refs/heads/prune-entire +' + test_expect_success '--remap-to-ancestor with filename filters' ' git checkout master && git reset --hard A && diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 072e6c6b88..b8ad076c25 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -879,18 +879,16 @@ test_expect_success GPG 'verifying a forged tag should fail' ' test_must_fail git tag -v forged-tag ' -test_expect_success 'verifying a proper tag with --format pass and format accordingly' ' - cat >expect <<-\EOF +test_expect_success GPG 'verifying a proper tag with --format pass and format accordingly' ' + cat >expect <<-\EOF && tagname : signed-tag - EOF && + EOF git tag -v --format="tagname : %(tag)" "signed-tag" >actual && test_cmp expect actual ' -test_expect_success 'verifying a forged tag with --format fail and format accordingly' ' - cat >expect <<-\EOF - tagname : forged-tag - EOF && +test_expect_success GPG 'verifying a forged tag with --format should fail silently' ' + >expect && test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual && test_cmp expect actual ' diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index d62ccbb98e..b4b49eeb08 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh @@ -125,18 +125,16 @@ test_expect_success GPG 'verify multiple tags' ' test_cmp expect.stderr actual.stderr ' -test_expect_success 'verifying tag with --format' ' - cat >expect <<-\EOF +test_expect_success GPG 'verifying tag with --format' ' + cat >expect <<-\EOF && tagname : fourth-signed - EOF && + EOF git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual && test_cmp expect actual ' -test_expect_success 'verifying a forged tag with --format fail and format accordingly' ' - cat >expect <<-\EOF - tagname : 7th forged-signed - EOF && +test_expect_success GPG 'verifying a forged tag with --format should fail silently' ' + >expect && test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged && test_cmp expect actual-forged ' diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh new file mode 100755 index 0000000000..b22f631261 --- /dev/null +++ b/t/t7518-ident-corner-cases.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='corner cases in ident strings' +. ./test-lib.sh + +# confirm that we do not segfault _and_ that we do not say "(null)", as +# glibc systems will quietly handle our NULL pointer +# +# Note also that we can't use "env" here because we need to unset a variable, +# and "-u" is not portable. +test_expect_success 'empty name and missing email' ' + ( + sane_unset GIT_AUTHOR_EMAIL && + GIT_AUTHOR_NAME= && + test_must_fail git commit --allow-empty -m foo 2>err && + test_i18ngrep ! null err + ) +' + +test_expect_success 'commit rejects all-crud name' ' + test_must_fail env GIT_AUTHOR_NAME=" .;<>" \ + git commit --allow-empty -m foo +' + +# We must test the actual error message here, as an unwanted +# auto-detection could fail for other reasons. +test_expect_success 'empty configured name does not auto-detect' ' + ( + sane_unset GIT_AUTHOR_NAME && + test_must_fail \ + git -c user.name= commit --allow-empty -m foo 2>err && + test_i18ngrep "empty ident name" err + ) +' + +test_done diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 19f0108f8a..cee42097b0 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -982,6 +982,72 @@ test_expect_success 'grep -e -- -- path' ' test_cmp expected actual ' +test_expect_success 'dashdash disambiguates rev as rev' ' + test_when_finished "rm -f master" && + echo content >master && + echo master:hello.c >expect && + git grep -l o master -- hello.c >actual && + test_cmp expect actual +' + +test_expect_success 'dashdash disambiguates pathspec as pathspec' ' + test_when_finished "git rm -f master" && + echo content >master && + git add master && + echo master:content >expect && + git grep o -- master >actual && + test_cmp expect actual +' + +test_expect_success 'report bogus arg without dashdash' ' + test_must_fail git grep o does-not-exist +' + +test_expect_success 'report bogus rev with dashdash' ' + test_must_fail git grep o hello.c -- +' + +test_expect_success 'allow non-existent path with dashdash' ' + # We need a real match so grep exits with success. + tree=$(git ls-tree HEAD | + sed s/hello.c/not-in-working-tree/ | + git mktree) && + git grep o "$tree" -- not-in-working-tree +' + +test_expect_success 'grep --no-index pattern -- path' ' + rm -fr non && + mkdir -p non/git && + ( + GIT_CEILING_DIRECTORIES="$(pwd)/non" && + export GIT_CEILING_DIRECTORIES && + cd non/git && + echo hello >hello && + echo goodbye >goodbye && + echo hello:hello >expect && + git grep --no-index o -- hello >actual && + test_cmp expect actual + ) +' + +test_expect_success 'grep --no-index complains of revs' ' + test_must_fail git grep --no-index o master -- 2>err && + test_i18ngrep "cannot be used with revs" err +' + +test_expect_success 'grep --no-index prefers paths to revs' ' + test_when_finished "rm -f master" && + echo content >master && + echo master:content >expect && + git grep --no-index o master >actual && + test_cmp expect actual +' + +test_expect_success 'grep --no-index does not "diagnose" revs' ' + test_must_fail git grep --no-index o :1:hello.c 2>err && + test_i18ngrep ! -i "did you mean" err +' + cat >expected <<EOF hello.c:int main(int argc, const char **argv) hello.c: printf("Hello world.\n"); diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 0f398dd160..60a80f60b2 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -148,7 +148,6 @@ cat >expected-cc <<\EOF !two@example.com! !three@example.com! !four@example.com! -!five@example.com! EOF " @@ -159,9 +158,9 @@ test_expect_success $PREREQ 'cc trailer with various syntax' ' Test Cc: trailers. Cc: one@example.com - Cc: <two@example.com> # this is part of the name - Cc: <three@example.com>, <four@example.com> # not.five@example.com - Cc: "Some # Body" <five@example.com> [part.of.name.too] + Cc: <two@example.com> # trailing comments are ignored + Cc: <three@example.com>, <not.four@example.com> one address per line + Cc: "Some # Body" <four@example.com> [ <also.a.comment> ] EOF clean_fake_sendmail && git send-email -1 --to=recipient@example.com \ diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index bb879a527d..1319415ba8 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -18,6 +18,11 @@ then test_done fi +if ! test_have_prereq NOT_ROOT; then + skip_all='When cvs is compiled with CVS_BADROOT commits as root fail' + test_done +fi + CVSROOT=$PWD/tmpcvsroot CVSWORK=$PWD/cvswork GIT_DIR=$PWD/.git diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 4c384ff023..804ce3850f 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -3,6 +3,11 @@ test_description='git cvsimport basic tests' . ./lib-cvs.sh +if ! test_have_prereq NOT_ROOT; then + skip_all='When cvs is compiled with CVS_BADROOT commits as root fail' + test_done +fi + test_expect_success PERL 'setup cvsroot environment' ' CVSROOT=$(pwd)/cvsroot && export CVSROOT diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index bd357704cc..5ee124332a 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -136,17 +136,12 @@ test_tick () { export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } -# Stop execution and start a shell. This is useful for debugging tests and -# only makes sense together with "-v". +# Stop execution and start a shell. This is useful for debugging tests. # # Be sure to remove all invocations of this command before submitting. test_pause () { - if test "$verbose" = t; then - "$SHELL_PATH" <&6 >&3 2>&4 - else - error >&5 "test_pause requires --verbose" - fi + "$SHELL_PATH" <&6 >&5 2>&7 } # Wrap git in gdb. Adding this to a command can make it easier to @@ -154,7 +149,7 @@ test_pause () { # # Example: "debug git checkout master". debug () { - GIT_TEST_GDB=1 "$@" + GIT_TEST_GDB=1 "$@" <&6 >&5 2>&7 } # Call test_commit with the arguments diff --git a/t/test-lib.sh b/t/test-lib.sh index 86d77c16dd..23c29bce6e 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -342,6 +342,7 @@ fi exec 5>&1 exec 6<&0 +exec 7>&2 if test "$verbose_log" = "t" then exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3 diff --git a/tempfile.c b/tempfile.c index ffcc272375..6843710670 100644 --- a/tempfile.c +++ b/tempfile.c @@ -247,8 +247,13 @@ int close_tempfile(struct tempfile *tempfile) tempfile->fd = -1; if (fp) { tempfile->fp = NULL; - err = ferror(fp); - err |= fclose(fp); + if (ferror(fp)) { + err = -1; + if (!fclose(fp)) + errno = EIO; + } else { + err = fclose(fp); + } } else { err = close(fd); } diff --git a/transport-helper.c b/transport-helper.c index 1258d6aedd..dc90a1fb76 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -124,8 +124,9 @@ static struct child_process *get_helper(struct transport *transport) helper->git_cmd = 0; helper->silent_exec_failure = 1; - argv_array_pushf(&helper->env_array, "%s=%s", GIT_DIR_ENVIRONMENT, - get_git_dir()); + if (have_git_dir()) + argv_array_pushf(&helper->env_array, "%s=%s", + GIT_DIR_ENVIRONMENT, get_git_dir()); code = start_command(helper); if (code < 0 && errno == ENOENT) diff --git a/transport.c b/transport.c index d72e089484..b58f5c8cdc 100644 --- a/transport.c +++ b/transport.c @@ -204,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus static int fetch_refs_via_pack(struct transport *transport, int nr_heads, struct ref **to_fetch) { + int ret = 0; struct git_transport_data *data = transport->data; struct ref *refs; char *dest = xstrdup(transport->url); @@ -241,19 +242,22 @@ static int fetch_refs_via_pack(struct transport *transport, &transport->pack_lockfile); close(data->fd[0]); close(data->fd[1]); - if (finish_connect(data->conn)) { - free_refs(refs); - refs = NULL; - } + if (finish_connect(data->conn)) + ret = -1; data->conn = NULL; data->got_remote_heads = 0; data->options.self_contained_and_connected = args.self_contained_and_connected; + if (refs == NULL) + ret = -1; + if (report_unmatched_refs(to_fetch, nr_heads)) + ret = -1; + free_refs(refs_tmp); free_refs(refs); free(dest); - return (refs ? 0 : -1); + return ret; } static int push_had_errors(struct ref *ref) @@ -1221,7 +1225,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e, const struct ref *extra; struct alternate_refs_data *cb = data; - other = real_pathdup(e->path); + other = real_pathdup(e->path, 1); len = strlen(other); while (other[len-1] == '/') diff --git a/upload-pack.c b/upload-pack.c index 7597ba3405..ffb028d623 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -822,9 +822,13 @@ static void receive_needs(void) use_include_tag = 1; o = parse_object(sha1_buf); - if (!o) + if (!o) { + packet_write_fmt(1, + "ERR upload-pack: not our ref %s", + sha1_to_hex(sha1_buf)); die("git upload-pack: not our ref %s", sha1_to_hex(sha1_buf)); + } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 diff --git a/worktree.c b/worktree.c index d633761575..fa7bc67a50 100644 --- a/worktree.c +++ b/worktree.c @@ -175,7 +175,7 @@ struct worktree **get_worktrees(unsigned flags) struct dirent *d; int counter = 0, alloc = 2; - list = xmalloc(alloc * sizeof(struct worktree *)); + ALLOC_ARRAY(list, alloc); list[counter++] = get_main_worktree(); @@ -255,7 +255,7 @@ struct worktree *find_worktree(struct worktree **list, return wt; arg = prefix_filename(prefix, strlen(prefix), arg); - path = real_pathdup(arg); + path = real_pathdup(arg, 1); for (; *list; list++) if (!fspathcmp(path, real_path((*list)->path))) break; @@ -440,23 +440,6 @@ int xmkstemp(char *template) return fd; } -/* git_mkstemp() - create tmp file honoring TMPDIR variable */ -int git_mkstemp(char *path, size_t len, const char *template) -{ - const char *tmp; - size_t n; - - tmp = getenv("TMPDIR"); - if (!tmp) - tmp = "/tmp"; - n = snprintf(path, len, "%s/%s", tmp, template); - if (len <= n) { - errno = ENAMETOOLONG; - return -1; - } - return mkstemp(path); -} - /* Adapted from libiberty's mkstemp.c. */ #undef TMP_MAX @@ -531,13 +514,6 @@ int git_mkstemp_mode(char *pattern, int mode) return git_mkstemps_mode(pattern, 0, mode); } -#ifdef NO_MKSTEMPS -int gitmkstemps(char *pattern, int suffix_len) -{ - return git_mkstemps_mode(pattern, suffix_len, 0600); -} -#endif - int xmkstemp_mode(char *template, int mode) { int fd; diff --git a/wt-status.c b/wt-status.c index d47012048f..234e77a6d6 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1730,12 +1730,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) return; branch_name = s->branch; +#define LABEL(string) (s->no_gettext ? (string) : _(string)) + if (s->is_initial) - color_fprintf(s->fp, header_color, _("Initial commit on ")); + color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on "))); if (!strcmp(s->branch, "HEAD")) { color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s", - _("HEAD (no branch)")); + LABEL(N_("HEAD (no branch)"))); goto conclude; } @@ -1760,8 +1762,6 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) if (!upstream_is_gone && !num_ours && !num_theirs) goto conclude; -#define LABEL(string) (s->no_gettext ? (string) : _(string)) - color_fprintf(s->fp, header_color, " ["); if (upstream_is_gone) { color_fprintf(s->fp, header_color, LABEL(N_("gone"))); |