diff options
52 files changed, 599 insertions, 302 deletions
@@ -13,6 +13,7 @@ Alex Riesen <raa.lkml@gmail.com> <raa@limbo.localdomain> Alex Riesen <raa.lkml@gmail.com> <raa@steel.home> Alex Vandiver <alex@chmrr.net> <alexmv@MIT.EDU> Alexander Gavrilov <angavrilov@gmail.com> +Alexander Kuleshov <kuleshovmail@gmail.com> Alexey Shumkin <alex.crezoff@gmail.com> <zapped@mail.ru> Alexey Shumkin <alex.crezoff@gmail.com> <Alex.Crezoff@gmail.com> Anders Kaseorg <andersk@MIT.EDU> <andersk@ksplice.com> diff --git a/Documentation/RelNotes/2.3.2.txt b/Documentation/RelNotes/2.3.2.txt index f4caf54927..93462e45c2 100644 --- a/Documentation/RelNotes/2.3.2.txt +++ b/Documentation/RelNotes/2.3.2.txt @@ -24,7 +24,7 @@ Fixes since v2.3.1 "curl-config --vernum", which confused our build system. * An earlier workaround to squelch unhelpful deprecation warnings - from the complier on Mac OSX unnecessarily set minimum required + from the compiler on Mac OSX unnecessarily set minimum required version of the OS, which the user might want to raise (or lower) for other reasons. diff --git a/Documentation/RelNotes/2.3.4.txt b/Documentation/RelNotes/2.3.4.txt new file mode 100644 index 0000000000..094c7b853b --- /dev/null +++ b/Documentation/RelNotes/2.3.4.txt @@ -0,0 +1,32 @@ +Git v2.3.4 Release Notes +======================== + +Fixes since v2.3.3 +------------------ + + * The 'color.status.unmerged' configuration was not described. + + * "git log --decorate" did not reset colors correctly around the + branch names. + + * "git -C '' subcmd" refused to work in the current directory, unlike + "cd ''" which silently behaves as a no-op. + + * "git imap-send" learned to optionally talk with an IMAP server via + libcURL; because there is no other option when Git is built with + NO_OPENSSL option, use that codepath by default under such + configuration. + + * A workaround for certain build of GPG that triggered false breakage + in a test has been added. + + * "git rebase -i" recently started to include the number of + commits in the insn sheet to be processed, but on a platform + that prepends leading whitespaces to "wc -l" output, the numbers + are shown with extra whitespaces that aren't necessary. + + * We did not parse username followed by literal IPv6 address in SSH + transport URLs, e.g. ssh://user@[2001:db8::1]:22/repo.git + correctly. + +Also contains typofixes, documentation updates and trivial code clean-ups. diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index ef0eeb40cd..98fc4cc1d0 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -136,6 +136,11 @@ that is fine, but please mark it as such. (4) Sending your patches. +Learn to use format-patch and send-email if possible. These commands +are optimized for the workflow of sending patches, avoiding many ways +your existing e-mail client that is optimized for "multipart/*" mime +type e-mails to corrupt and render your patches unusable. + People on the Git mailing list need to be able to read and comment on the changes you are submitting. It is important for a developer to be able to "quote" your changes, using standard diff --git a/Documentation/config.txt b/Documentation/config.txt index 5fbe9403b8..1a8ddb41c7 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -14,7 +14,8 @@ the fully qualified variable name of the variable itself is the last dot-separated segment and the section name is everything before the last dot. The variable names are case-insensitive, allow only alphanumeric characters and `-`, and must start with an alphabetic character. Some -variables may appear multiple times. +variables may appear multiple times; we say then that the variable is +multivalued. Syntax ~~~~~~ @@ -25,7 +26,7 @@ blank lines are ignored. The file consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next -section begins. Section names are not case sensitive. Only alphanumeric +section begins. Section names are case-insensitive. Only alphanumeric characters, `-` and `.` are allowed in section names. Each variable must belong to some section, which means that there must be a section header before the first setting of a variable. @@ -40,8 +41,8 @@ in the section header, like in the example below: -------- Subsection names are case sensitive and can contain any characters except -newline (doublequote `"` and backslash have to be escaped as `\"` and `\\`, -respectively). Section headers cannot span multiple +newline (doublequote `"` and backslash can be included by escaping them +as `\"` and `\\`, respectively). Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you don't need to. @@ -53,38 +54,27 @@ restrictions as section names. All the other lines (and the remainder of the line after the section header) are recognized as setting variables, in the form -'name = value'. If there is no equal sign on the line, the entire line -is taken as 'name' and the variable is recognized as boolean "true". +'name = value' (or just 'name', which is a short-hand to say that +the variable is the boolean "true"). The variable names are case-insensitive, allow only alphanumeric characters -and `-`, and must start with an alphabetic character. There can be more -than one value for a given variable; we say then that the variable is -multivalued. +and `-`, and must start with an alphabetic character. -Leading and trailing whitespace in a variable value is discarded. -Internal whitespace within a variable value is retained verbatim. +A line that defines a value can be continued to the next line by +ending it with a `\`; the backquote and the end-of-line are +stripped. Leading whitespaces after 'name =', the remainder of the +line after the first comment character '#' or ';', and trailing +whitespaces of the line are discarded unless they are enclosed in +double quotes. Internal whitespaces within the value are retained +verbatim. -The values following the equals sign in variable assign are all either -a string, an integer, or a boolean. Boolean values may be given as yes/no, -1/0, true/false or on/off. Case is not significant in boolean values, when -converting value to the canonical form using '--bool' type specifier; -'git config' will ensure that the output is "true" or "false". - -String values may be entirely or partially enclosed in double quotes. -You need to enclose variable values in double quotes if you want to -preserve leading or trailing whitespace, or if the variable value contains -comment characters (i.e. it contains '#' or ';'). -Double quote `"` and backslash `\` characters in variable values must -be escaped: use `\"` for `"` and `\\` for `\`. +Inside double quotes, double quote `"` and backslash `\` characters +must be escaped: use `\"` for `"` and `\\` for `\`. The following escape sequences (beside `\"` and `\\`) are recognized: `\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) and `\b` for backspace (BS). Other char escape sequences (including octal escape sequences) are invalid. -Variable values ending in a `\` are continued on the next line in the -customary UNIX fashion. - -Some variables may require a special value format. Includes ~~~~~~~~ @@ -126,6 +116,61 @@ Example path = foo ; expand "foo" relative to the current file path = ~/foo ; expand "foo" in your $HOME directory + +Values +~~~~~~ + +Values of many variables are treated as a simple string, but there +are variables that take values of specific types and there are rules +as to how to spell them. + +boolean:: + + When a variable is said to take a boolean value, many + synonyms are accepted for 'true' and 'false'; these are all + case-insensitive. + + true;; Boolean true can be spelled as `yes`, `on`, `true`, + or `1`. Also, a variable defined without `= <value>` + is taken as true. + + false;; Boolean false can be spelled as `no`, `off`, + `false`, or `0`. ++ +When converting value to the canonical form using '--bool' type +specifier; 'git config' will ensure that the output is "true" or +"false" (spelled in lowercase). + +integer:: + The value for many variables that specify various sizes can + be suffixed with `k`, `M`,... to mean "scale the number by + 1024", "by 1024x1024", etc. + +color:: + The value for a variables that takes a color is a list of + colors (at most two) and attributes (at most one), separated + by spaces. The colors accepted are `normal`, `black`, + `red`, `green`, `yellow`, `blue`, `magenta`, `cyan` and + `white`; the attributes are `bold`, `dim`, `ul`, `blink` and + `reverse`. The first color given is the foreground; the + second is the background. The position of the attribute, if + any, doesn't matter. Attributes may be turned off + specifically by prefixing them with `no` (e.g., `noreverse`, + `noul`, etc). ++ +Colors (foreground and background) may also be given as numbers between +0 and 255; these use ANSI 256-color mode (but note that not all +terminals may support this). If your terminal supports it, you may also +specify 24-bit RGB values as hex, like `#ff0ab3`. ++ +The attributes are meant to be reset at the beginning of each item +in the colored output, so setting color.decorate.branch to `black` +will paint that branch name in a plain `black`, even if the previous +thing on the same output line (e.g. opening parenthesis before the +list of branch names in `log --decorate` output) is set to be +painted with `bold` or some other attribute. + + Variables ~~~~~~~~~ @@ -846,20 +891,6 @@ color.branch.<slot>:: `remote` (a remote-tracking branch in refs/remotes/), `upstream` (upstream tracking branch), `plain` (other refs). -+ -The value for these configuration variables is a list of colors (at most -two) and attributes (at most one), separated by spaces. The colors -accepted are `normal`, `black`, `red`, `green`, `yellow`, `blue`, -`magenta`, `cyan` and `white`; the attributes are `bold`, `dim`, `ul`, -`blink` and `reverse`. The first color given is the foreground; the -second is the background. The position of the attribute, if any, -doesn't matter. Attributes may be turned off specifically by prefixing -them with `no` (e.g., `noreverse`, `noul`, etc). -+ -Colors (foreground and background) may also be given as numbers between -0 and 255; these use ANSI 256-color mode (but note that not all -terminals may support this). If your terminal supports it, you may also -specify 24-bit RGB values as hex, like `#ff0ab3`. color.diff:: Whether to use ANSI escape sequences to add color to patches. @@ -879,8 +910,7 @@ color.diff.<slot>:: of `plain` (context text), `meta` (metainformation), `frag` (hunk header), 'func' (function in hunk header), `old` (removed lines), `new` (added lines), `commit` (commit headers), or `whitespace` - (highlighting whitespace errors). The values of these variables may be - specified as in color.branch.<slot>. + (highlighting whitespace errors). color.decorate.<slot>:: Use customized color for 'git log --decorate' output. `<slot>` is one @@ -917,8 +947,6 @@ color.grep.<slot>:: separators between fields on a line (`:`, `-`, and `=`) and between hunks (`--`) -- -+ -The values of these variables may be specified as in color.branch.<slot>. color.interactive:: When set to `always`, always use colors for interactive prompts @@ -931,8 +959,7 @@ color.interactive.<slot>:: Use customized color for 'git add --interactive' and 'git clean --interactive' output. `<slot>` may be `prompt`, `header`, `help` or `error`, for four distinct types of normal output from - interactive commands. The values of these variables may be - specified as in color.branch.<slot>. + interactive commands. color.pager:: A boolean to enable/disable colored output when the pager is in @@ -956,10 +983,10 @@ color.status.<slot>:: `added` or `updated` (files which are added but not committed), `changed` (files which are changed but not added in the index), `untracked` (files which are not tracked by Git), - `branch` (the current branch), or + `branch` (the current branch), `nobranch` (the color the 'no branch' warning is shown in, defaulting - to red). The values of these variables may be specified as in - color.branch.<slot>. + to red), or + `unmerged` (files which have unmerged changes). color.ui:: This variable determines the default value for variables such diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index 77aacf1309..5d1e4c80cd 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -44,7 +44,8 @@ OPTIONS --no-curl:: Talk to the IMAP server using git's own IMAP routines instead of - using libcurl. + using libcurl. Ignored if Git was built with the NO_OPENSSL option + set. CONFIGURATION diff --git a/Documentation/git.txt b/Documentation/git.txt index af306209e9..bfa19cb50b 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ 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.3.3/git.html[documentation for release 2.3.3] +* link:v2.3.4/git.html[documentation for release 2.3.4] * release notes for + link:RelNotes/2.3.4.txt[2.3.4], link:RelNotes/2.3.3.txt[2.3.3], link:RelNotes/2.3.2.txt[2.3.2], link:RelNotes/2.3.1.txt[2.3.1], @@ -768,7 +769,8 @@ Git so take care if using Cogito etc. 'GIT_INDEX_VERSION':: This environment variable allows the specification of an index version for new repositories. It won't affect existing index - files. By default index file version [23] is used. + files. By default index file version 2 or 3 is used. See + linkgit:git-update-index[1] for more information. 'GIT_OBJECT_DIRECTORY':: If the object storage directory is specified via this @@ -1026,6 +1028,17 @@ GIT_ICASE_PATHSPECS:: variable when it is invoked as the top level command by the end user, to be recorded in the body of the reflog. +`GIT_REF_PARANOIA`:: + If set to `1`, include broken or badly named refs when iterating + over lists of refs. In a normal, non-corrupted repository, this + does nothing. However, enabling it may help git to detect and + abort some operations in the presence of broken refs. Git sets + this variable automatically when performing destructive + operations like linkgit:git-prune[1]. You should not need to set + it yourself unless you want to be paranoid about making sure + an operation has touched every ref (e.g., because you are + cloning a repository to make a backup). + Discussion[[Discussion]] ------------------------ diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index f92fe17065..9905751fc2 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.3.3 +DEF_VER=v2.3.4 LF=' ' @@ -1 +1 @@ -Documentation/RelNotes/2.3.3.txt
\ No newline at end of file +Documentation/RelNotes/2.3.4.txt
\ No newline at end of file diff --git a/archive-zip.c b/archive-zip.c index 4bde019bce..1a54e1b187 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -120,7 +120,6 @@ static void *zlib_deflate_raw(void *data, unsigned long size, void *buffer; int result; - memset(&stream, 0, sizeof(stream)); git_deflate_init_raw(&stream, compression_level); maxsize = git_deflate_bound(&stream, size); buffer = xmalloc(maxsize); @@ -349,7 +348,6 @@ static int write_zip_entry(struct archiver_args *args, size_t out_len; unsigned char compressed[STREAM_BUFFER_SIZE * 2]; - memset(&zstream, 0, sizeof(zstream)); git_deflate_init_raw(&zstream, args->compression_level); compressed_size = 0; diff --git a/builtin/clone.c b/builtin/clone.c index 316c75d0b3..2d77a2de4f 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -842,20 +842,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix) git_dir = mkpathdup("%s/.git", dir); } + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + if (!option_bare) { - junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0777)) - die_errno(_("could not create work tree dir '%s'."), + die_errno(_("could not create work tree dir '%s'"), work_tree); + junk_work_tree = work_tree; set_git_work_tree(work_tree); } - junk_git_dir = git_dir; - atexit(remove_junk); - sigchain_push_common(remove_junk_on_signal); + junk_git_dir = git_dir; if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); diff --git a/builtin/grep.c b/builtin/grep.c index e77f7cf109..fe7b9fdd93 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -885,7 +885,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } - if (!show_in_pager) + if (!show_in_pager && !opt.status_only) setup_pager(); if (!use_index && (untracked || cached)) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 4632117671..cf654df09b 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1204,7 +1204,6 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size) int status; unsigned char outbuf[4096]; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); stream.next_in = in; stream.avail_in = size; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d8165878e1..c3a75166bd 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -125,7 +125,6 @@ static unsigned long do_compress(void **pptr, unsigned long size) void *in, *out; unsigned long maxsize; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, pack_compression_level); maxsize = git_deflate_bound(&stream, size); @@ -153,7 +152,6 @@ static unsigned long write_large_blob_data(struct git_istream *st, struct sha1fi unsigned char obuf[1024 * 16]; unsigned long olen = 0; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, pack_compression_level); for (;;) { diff --git a/builtin/prune.c b/builtin/prune.c index 04d3b12ae4..17094ad954 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -115,6 +115,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = ULONG_MAX; save_commit_buffer = 0; check_replace_refs = 0; + ref_paranoia = 1; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); diff --git a/builtin/repack.c b/builtin/repack.c index 3f852f35d1..2fe1b30d71 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -228,13 +228,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix) get_non_kept_pack_filenames(&existing_packs); if (existing_packs.nr && delete_redundant) { - if (unpack_unreachable) + if (unpack_unreachable) { argv_array_pushf(&cmd.args, "--unpack-unreachable=%s", unpack_unreachable); - else if (pack_everything & LOOSEN_UNREACHABLE) + argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); + } else if (pack_everything & LOOSEN_UNREACHABLE) { argv_array_push(&cmd.args, "--unpack-unreachable"); + } else { + argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); + } } } else { argv_array_push(&cmd.args, "--unpacked"); diff --git a/builtin/tag.c b/builtin/tag.c index e633f4efdb..d315827cf5 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -605,13 +605,13 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), + + OPT_GROUP(N_("Tag listing options")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), { OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"), PARSE_OPT_NONEG, parse_opt_sort }, - - OPT_GROUP(N_("Tag listing options")), { OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"), N_("print only tags that contain the commit"), diff --git a/bulk-checkin.c b/bulk-checkin.c index 0c4b8a7cad..8d157eba45 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -105,7 +105,6 @@ static int stream_to_pack(struct bulk_checkin_state *state, int write_object = (flags & HASH_WRITE_OBJECT); off_t offset = 0; - memset(&s, 0, sizeof(s)); git_deflate_init(&s, pack_compression_level); hdrlen = encode_in_pack_object_header(type, size, obuf); @@ -614,6 +614,14 @@ extern int protect_hfs; extern int protect_ntfs; /* + * Include broken refs in all ref iterations, which will + * generally choke dangerous operations rather than letting + * them silently proceed without taking the broken ref into + * account. + */ +extern int ref_paranoia; + +/* * The character that begins a commented line in user-editable file * that is subject to stripspace. */ @@ -273,28 +273,44 @@ static enum protocol get_protocol(const char *name) die("I don't handle protocol '%s'", name); } +static char *host_end(char **hoststart, int removebrackets) +{ + char *host = *hoststart; + char *end; + char *start = strstr(host, "@["); + if (start) + start++; /* Jump over '@' */ + else + start = host; + if (start[0] == '[') { + end = strchr(start + 1, ']'); + if (end) { + if (removebrackets) { + *end = 0; + memmove(start, start + 1, end - start); + end++; + } + } else + end = host; + } else + end = host; + return end; +} + #define STR_(s) # s #define STR(s) STR_(s) static void get_host_and_port(char **host, const char **port) { char *colon, *end; - - if (*host[0] == '[') { - end = strchr(*host + 1, ']'); - if (end) { - *end = 0; - end++; - (*host)++; - } else - end = *host; - } else - end = *host; + end = host_end(host, 1); colon = strchr(end, ':'); - if (colon) { - *colon = 0; - *port = colon + 1; + long portnr = strtol(colon + 1, &end, 10); + if (end != colon + 1 && *end == '\0' && 0 <= portnr && portnr < 65536) { + *colon = 0; + *port = colon + 1; + } } } @@ -546,13 +562,16 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) return proxy; } -static const char *get_port_numeric(const char *p) +static char *get_port(char *host) { char *end; + char *p = strchr(host, ':'); + if (p) { long port = strtol(p + 1, &end, 10); if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) { - return p; + *p = '\0'; + return p+1; } } @@ -594,14 +613,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, * Don't do destructive transforms as protocol code does * '[]' unwrapping in get_host_and_port() */ - if (host[0] == '[') { - end = strchr(host + 1, ']'); - if (end) { - end++; - } else - end = host; - } else - end = host; + end = host_end(&host, 0); if (protocol == PROTO_LOCAL) path = end; @@ -662,7 +674,7 @@ struct child_process *git_connect(int fd[2], const char *url, signal(SIGCHLD, SIG_DFL); protocol = parse_connect_url(url, &hostandport, &path); - if (flags & CONNECT_DIAG_URL) { + if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); @@ -714,28 +726,42 @@ struct child_process *git_connect(int fd[2], const char *url, char *ssh_host = hostandport; const char *port = NULL; get_host_and_port(&ssh_host, &port); - port = get_port_numeric(port); - ssh = getenv("GIT_SSH_COMMAND"); - if (ssh) { - conn->use_shell = 1; - putty = 0; - } else { - ssh = getenv("GIT_SSH"); - if (!ssh) - ssh = "ssh"; - putty = !!strcasestr(ssh, "plink"); - } + if (!port) + port = get_port(ssh_host); + + if (flags & CONNECT_DIAG_URL) { + printf("Diag: url=%s\n", url ? url : "NULL"); + printf("Diag: protocol=%s\n", prot_name(protocol)); + printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); + printf("Diag: port=%s\n", port ? port : "NONE"); + printf("Diag: path=%s\n", path ? path : "NULL"); - argv_array_push(&conn->args, ssh); - if (putty && !strcasestr(ssh, "tortoiseplink")) - argv_array_push(&conn->args, "-batch"); - if (port) { - /* P is for PuTTY, p is for OpenSSH */ - argv_array_push(&conn->args, putty ? "-P" : "-p"); - argv_array_push(&conn->args, port); + free(hostandport); + free(path); + return NULL; + } else { + ssh = getenv("GIT_SSH_COMMAND"); + if (ssh) { + conn->use_shell = 1; + putty = 0; + } else { + ssh = getenv("GIT_SSH"); + if (!ssh) + ssh = "ssh"; + putty = !!strcasestr(ssh, "plink"); + } + + argv_array_push(&conn->args, ssh); + if (putty && !strcasestr(ssh, "tortoiseplink")) + argv_array_push(&conn->args, "-batch"); + if (port) { + /* P is for PuTTY, p is for OpenSSH */ + argv_array_push(&conn->args, putty ? "-P" : "-p"); + argv_array_push(&conn->args, port); + } + argv_array_push(&conn->args, ssh_host); } - argv_array_push(&conn->args, ssh_host); } else { /* remove repo-local variables from the environment */ conn->env = local_repo_env; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 05a3aa426f..661a8294da 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -411,12 +411,9 @@ __git_refs_remotes () __git_remotes () { - local i IFS=$'\n' d="$(__gitdir)" + local d="$(__gitdir)" test -d "$d/remotes" && ls -1 "$d/remotes" - for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do - i="${i#remote.}" - echo "${i/.url*/}" - done + git --git-dir="$d" remote } __git_list_merge_strategies () @@ -1305,7 +1302,7 @@ _git_gitk () } __git_match_ctag() { - awk "/^${1////\\/}/ { print \$1 }" "$2" + awk "/^${1//\//\\/}/ { print \$1 }" "$2" } _git_grep () @@ -2014,6 +2011,7 @@ _git_config () color.status.changed color.status.header color.status.nobranch + color.status.unmerged color.status.untracked color.status.updated color.ui diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 214e859f99..f18aedc73b 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -487,7 +487,7 @@ __git_ps1 () if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] && [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] && - git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null + git ls-files --others --exclude-standard --error-unmatch -- ':/*' >/dev/null 2>/dev/null then u="%${ZSH_VERSION+%}" fi diff --git a/csum-file.c b/csum-file.c index b00b215031..a172199e44 100644 --- a/csum-file.c +++ b/csum-file.c @@ -130,14 +130,10 @@ struct sha1file *sha1fd_check(const char *name) sink = open("/dev/null", O_WRONLY); if (sink < 0) - return NULL; + die_errno("unable to open /dev/null"); check = open(name, O_RDONLY); - if (check < 0) { - int saved_errno = errno; - close(sink); - errno = saved_errno; - return NULL; - } + if (check < 0) + die_errno("unable to open '%s'", name); f = sha1fd(sink, name); f->check_fd = check; return f; @@ -2093,7 +2093,6 @@ static unsigned char *deflate_it(char *data, unsigned char *deflated; git_zstream stream; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); bound = git_deflate_bound(&stream, size); deflated = xmalloc(bound); diff --git a/environment.c b/environment.c index 1ade5c9684..a40044c3bf 100644 --- a/environment.c +++ b/environment.c @@ -24,6 +24,7 @@ int is_bare_repository_cfg = -1; /* unspecified */ int log_all_ref_updates = -1; /* unspecified */ int warn_ambiguous_refs = 1; int warn_on_object_refname_ambiguity = 1; +int ref_paranoia = -1; int repository_format_version; const char *git_commit_encoding; const char *git_log_output_encoding; diff --git a/fast-import.c b/fast-import.c index aac2c24f74..77fb2ff368 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1062,7 +1062,6 @@ static int store_object( } else delta = NULL; - memset(&s, 0, sizeof(s)); git_deflate_init(&s, pack_compression_level); if (delta) { s.next_in = delta; @@ -1090,7 +1089,6 @@ static int store_object( free(delta); delta = NULL; - memset(&s, 0, sizeof(s)); git_deflate_init(&s, pack_compression_level); s.next_in = (void *)dat->buf; s.avail_in = dat->len; @@ -1190,7 +1188,6 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark) crc32_begin(pack_file); - memset(&s, 0, sizeof(s)); git_deflate_init(&s, pack_compression_level); hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf); diff --git a/fetch-pack.c b/fetch-pack.c index 655ee64256..48526aa54b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -544,16 +544,19 @@ static void filter_refs(struct fetch_pack_args *args, /* Append unmatched requests to the list */ if (allow_tip_sha1_in_want) { for (i = 0; i < nr_sought; i++) { + unsigned char sha1[20]; + ref = sought[i]; if (ref->matched) continue; - if (get_sha1_hex(ref->name, ref->old_sha1)) + if (get_sha1_hex(ref->name, sha1) || + ref->name[40] != '\0' || + hashcmp(sha1, ref->old_sha1)) continue; ref->matched = 1; - *newtail = ref; - ref->next = NULL; - newtail = &ref->next; + *newtail = copy_ref(ref); + newtail = &(*newtail)->next; } } *refs = newlist; @@ -625,7 +628,6 @@ static int everything_local(struct fetch_pack_args *args, for (retval = 1, ref = *refs; ref ; ref = ref->next) { const unsigned char *remote = ref->old_sha1; - unsigned char local[20]; struct object *o; o = lookup_object(remote); @@ -638,8 +640,6 @@ static int everything_local(struct fetch_pack_args *args, ref->name); continue; } - - hashcpy(ref->new_sha1, local); if (!args->verbose) continue; fprintf(stderr, diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c96b9847e9..f7deeb096e 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -1030,10 +1030,11 @@ test -n "$autosquash" && rearrange_squash "$todo" test -n "$cmd" && add_exec_commands "$todo" todocount=$(git stripspace --strip-comments <"$todo" | wc -l) +todocount=${todocount##* } cat >>"$todo" <<EOF -$comment_char Rebase $shortrevisions onto $shortonto ($todocount TODO item(s)) +$comment_char Rebase $shortrevisions onto $shortonto ($todocount command(s)) EOF append_todo_help git stripspace --comment-lines >>"$todo" <<\EOF @@ -204,10 +204,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) fprintf(stderr, "No directory given for -C.\n" ); usage(git_usage_string); } - if (chdir((*argv)[1])) - die_errno("Cannot change to '%s'", (*argv)[1]); - if (envchanged) - *envchanged = 1; + if ((*argv)[1][0]) { + if (chdir((*argv)[1])) + die_errno("Cannot change to '%s'", (*argv)[1]); + if (envchanged) + *envchanged = 1; + } (*argv)++; (*argc)--; } else { @@ -618,6 +620,7 @@ int main(int argc, char **av) { const char **argv = (const char **) av; const char *cmd; + int done_help = 0; startup_info = &git_startup_info; @@ -680,9 +683,7 @@ int main(int argc, char **av) setup_path(); while (1) { - static int done_help = 0; - static int was_alias = 0; - was_alias = run_argv(&argc, &argv); + int was_alias = run_argv(&argc, &argv); if (errno != ENOENT) break; if (was_alias) { diff --git a/http-push.c b/http-push.c index 0beb7ab67f..bfb1c9605b 100644 --- a/http-push.c +++ b/http-push.c @@ -365,7 +365,6 @@ static void start_put(struct transfer_request *request) hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; /* Set it up */ - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); size = git_deflate_bound(&stream, len + hdrlen); strbuf_init(&request->buffer.buf, size); diff --git a/imap-send.c b/imap-send.c index d69887da5a..37ac4aa86a 100644 --- a/imap-send.c +++ b/imap-send.c @@ -34,8 +34,16 @@ typedef void *SSL; #include "http.h" #endif +#if defined(USE_CURL_FOR_IMAP_SEND) && defined(NO_OPENSSL) +/* only available option */ +#define USE_CURL_DEFAULT 1 +#else +/* strictly opt in */ +#define USE_CURL_DEFAULT 0 +#endif + static int verbosity; -static int use_curl; /* strictly opt in */ +static int use_curl = USE_CURL_DEFAULT; static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL }; @@ -1504,9 +1512,14 @@ int main(int argc, char **argv) #ifndef USE_CURL_FOR_IMAP_SEND if (use_curl) { - warning("--use-curl not supported in this build"); + warning("--curl not supported in this build"); use_curl = 0; } +#elif defined(NO_OPENSSL) + if (!use_curl) { + warning("--no-curl not supported in this build"); + use_curl = 1; + } #endif if (!server.port) diff --git a/log-tree.c b/log-tree.c index 7f0890e4ac..53bb52659b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -195,6 +195,7 @@ void format_decorations_extended(struct strbuf *sb, while (decoration) { strbuf_addstr(sb, color_commit); strbuf_addstr(sb, prefix); + strbuf_addstr(sb, color_reset); strbuf_addstr(sb, decorate_get_color(use_color, decoration->type)); if (decoration->type == DECORATION_REF_TAG) strbuf_addstr(sb, "tag: "); diff --git a/read-cache.c b/read-cache.c index 8d71860f69..1bf78a445f 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1486,18 +1486,25 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk, return ce; } -static void check_ce_order(struct cache_entry *ce, struct cache_entry *next_ce) -{ - int name_compare = strcmp(ce->name, next_ce->name); - if (0 < name_compare) - die("unordered stage entries in index"); - if (!name_compare) { - if (!ce_stage(ce)) - die("multiple stage entries for merged file '%s'", - ce->name); - if (ce_stage(ce) > ce_stage(next_ce)) - die("unordered stage entries for '%s'", - ce->name); +static void check_ce_order(struct index_state *istate) +{ + unsigned int i; + + for (i = 1; i < istate->cache_nr; i++) { + struct cache_entry *ce = istate->cache[i - 1]; + struct cache_entry *next_ce = istate->cache[i]; + int name_compare = strcmp(ce->name, next_ce->name); + + if (0 < name_compare) + die("unordered stage entries in index"); + if (!name_compare) { + if (!ce_stage(ce)) + die("multiple stage entries for merged file '%s'", + ce->name); + if (ce_stage(ce) > ce_stage(next_ce)) + die("unordered stage entries for '%s'", + ce->name); + } } } @@ -1562,9 +1569,6 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) ce = create_from_disk(disk_ce, &consumed, previous_name); set_index_entry(istate, i, ce); - if (i > 0) - check_ce_order(istate->cache[i - 1], ce); - src_offset += consumed; } strbuf_release(&previous_name_buf); @@ -1608,11 +1612,10 @@ int read_index_from(struct index_state *istate, const char *path) ret = do_read_index(istate, path, 0); split_index = istate->split_index; - if (!split_index) - return ret; - - if (is_null_sha1(split_index->base_sha1)) + if (!split_index || is_null_sha1(split_index->base_sha1)) { + check_ce_order(istate); return ret; + } if (split_index->base) discard_index(split_index->base); @@ -1628,6 +1631,7 @@ int read_index_from(struct index_state *istate, const char *path) sha1_to_hex(split_index->base_sha1)), sha1_to_hex(split_index->base->sha1)); merge_base_index(istate); + check_ce_order(istate); return ret; } @@ -1907,6 +1907,11 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base, data.fn = fn; data.cb_data = cb_data; + if (ref_paranoia < 0) + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); + if (ref_paranoia) + data.flags |= DO_FOR_EACH_INCLUDE_BROKEN; + return do_for_each_entry(refs, base, do_one_ref, &data); } @@ -2591,68 +2596,10 @@ int pack_refs(unsigned int flags) return 0; } -/* - * If entry is no longer needed in packed-refs, add it to the string - * list pointed to by cb_data. Reasons for deleting entries: - * - * - Entry is broken. - * - Entry is overridden by a loose ref. - * - Entry does not point at a valid object. - * - * In the first and third cases, also emit an error message because these - * are indications of repository corruption. - */ -static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) -{ - struct string_list *refs_to_delete = cb_data; - - if (entry->flag & REF_ISBROKEN) { - /* This shouldn't happen to packed refs. */ - error("%s is broken!", entry->name); - string_list_append(refs_to_delete, entry->name); - return 0; - } - if (!has_sha1_file(entry->u.value.sha1)) { - unsigned char sha1[20]; - int flags; - - if (read_ref_full(entry->name, 0, sha1, &flags)) - /* We should at least have found the packed ref. */ - die("Internal error"); - if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) { - /* - * This packed reference is overridden by a - * loose reference, so it is OK that its value - * is no longer valid; for example, it might - * refer to an object that has been garbage - * collected. For this purpose we don't even - * care whether the loose reference itself is - * invalid, broken, symbolic, etc. Silently - * remove the packed reference. - */ - string_list_append(refs_to_delete, entry->name); - return 0; - } - /* - * There is no overriding loose reference, so the fact - * that this reference doesn't refer to a valid object - * indicates some kind of repository corruption. - * Report the problem, then omit the reference from - * the output. - */ - error("%s does not point to a valid object!", entry->name); - string_list_append(refs_to_delete, entry->name); - return 0; - } - - return 0; -} - int repack_without_refs(struct string_list *refnames, struct strbuf *err) { struct ref_dir *packed; - struct string_list refs_to_delete = STRING_LIST_INIT_DUP; - struct string_list_item *refname, *ref_to_delete; + struct string_list_item *refname; int ret, needs_repacking = 0, removed = 0; assert(err); @@ -2688,13 +2635,6 @@ int repack_without_refs(struct string_list *refnames, struct strbuf *err) return 0; } - /* Remove any other accumulated cruft */ - do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete); - for_each_string_list_item(ref_to_delete, &refs_to_delete) { - if (remove_entry(packed, ref_to_delete->string) == -1) - die("internal error"); - } - /* Write what remains */ ret = commit_packed_refs(); if (ret) diff --git a/remote-curl.c b/remote-curl.c index 515ac9b411..696a5ecf05 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -567,7 +567,6 @@ retry: git_zstream stream; int ret; - memset(&stream, 0, sizeof(stream)); git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION); gzip_size = git_deflate_bound(&stream, rpc->len); gzip_body = xmalloc(gzip_size); diff --git a/sha1_file.c b/sha1_file.c index 69a60ec88b..88f06bac92 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2943,7 +2943,6 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, } /* Set it up */ - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); stream.next_out = compressed; stream.avail_out = sizeof(compressed); diff --git a/sha1_name.c b/sha1_name.c index 95f9f8fa1a..6d10f052b5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -757,7 +757,7 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1) for (cp = name + len - 1; name + 2 <= cp; cp--) { char ch = *cp; - if (hexval(ch) & ~0377) { + if (!isxdigit(ch)) { /* We must be looking at g in "SOMETHING-g" * for it to be describe output. */ diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh index 99c037703a..2630e756da 100755 --- a/t/t0056-git-C.sh +++ b/t/t0056-git-C.sh @@ -14,6 +14,16 @@ test_expect_success '"git -C <path>" runs git from the directory <path>' ' test_cmp expected actual ' +test_expect_success '"git -C <path>" with an empty <path> is a no-op' ' + ( + mkdir -p dir1/subdir && + cd dir1/subdir && + git -C "" rev-parse --show-prefix >actual && + echo subdir/ >expect && + test_cmp expect actual + ) +' + test_expect_success 'Multiple -C options: "-C dir1 -C dir2" is equivalent to "-C dir1/dir2"' ' test_create_repo dir1/dir2 && echo 1 >dir1/dir2/b.txt && diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 94fb473e7c..92f7298fba 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -10,9 +10,18 @@ sane_unset GIT_TEST_SPLIT_INDEX test_expect_success 'enable split index' ' git update-index --split-index && test-dump-split-index .git/index >actual && + indexversion=$(test-index-version <.git/index) && + if test "$indexversion" = "4" + then + own=432ef4b63f32193984f339431fd50ca796493569 + base=508851a7f0dfa8691e9f69c7f055865389012491 + else + own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339 + base=39d890139ee5356c7ef572216cebcd27aa41f9df + fi && cat >expect <<EOF && -own 8299b0bcd1ac364e5f1d7768efb62fa2da79a339 -base 39d890139ee5356c7ef572216cebcd27aa41f9df +own $own +base $base replacements: deletions: EOF @@ -30,7 +39,7 @@ EOF test-dump-split-index .git/index | sed "/^own/d" >actual && cat >expect <<EOF && -base 39d890139ee5356c7ef572216cebcd27aa41f9df +base $base 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one replacements: deletions: diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index a31f7e0430..034eb35cdf 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1046,4 +1046,13 @@ test_expect_success 'respect core.abbrev' ' test 4 = $(grep -c "pick [0-9a-f]\{12,\}" todo-list) ' +test_expect_success 'todo count' ' + write_script dump-raw.sh <<-\EOF && + cat "$1" + EOF + test_set_editor "$(pwd)/dump-raw.sh" && + git rebase -i HEAD~4 >actual && + grep "^# Rebase ..* onto ..* ([0-9]" actual +' + test_done diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh index 925f577a3c..6b8ad4f5b0 100755 --- a/t/t4207-log-decoration-colors.sh +++ b/t/t4207-log-decoration-colors.sh @@ -44,15 +44,15 @@ test_expect_success setup ' ' cat >expected <<EOF -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\ - ${c_tag}tag: v1.0${c_reset}${c_commit},\ - ${c_tag}tag: B${c_reset}${c_commit},\ - ${c_branch}master${c_reset}${c_commit})${c_reset} B -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\ - ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1 -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\ +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}${c_commit},\ + ${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit},\ + ${c_reset}${c_tag}tag: B${c_reset}${c_commit},\ + ${c_reset}${c_branch}master${c_reset}${c_commit})${c_reset} B +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit},\ + ${c_reset}${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1 +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\ On master: Changes to A.t -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A EOF # We want log to show all, but the second parent to refs/stash is irrelevant diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh new file mode 100755 index 0000000000..8e98b44083 --- /dev/null +++ b/t/t5312-prune-corruption.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +test_description=' +Test pruning of repositories with minor corruptions. The goal +here is that we should always be erring on the side of safety. So +if we see, for example, a ref with a bogus name, it is OK either to +bail out or to proceed using it as a reachable tip, but it is _not_ +OK to proceed as if it did not exist. Otherwise we might silently +delete objects that cannot be recovered. +' +. ./test-lib.sh + +test_expect_success 'disable reflogs' ' + git config core.logallrefupdates false && + rm -rf .git/logs +' + +test_expect_success 'create history reachable only from a bogus-named ref' ' + test_tick && git commit --allow-empty -m master && + base=$(git rev-parse HEAD) && + test_tick && git commit --allow-empty -m bogus && + bogus=$(git rev-parse HEAD) && + git cat-file commit $bogus >saved && + echo $bogus >.git/refs/heads/bogus..name && + git reset --hard HEAD^ +' + +test_expect_success 'pruning does not drop bogus object' ' + test_when_finished "git hash-object -w -t commit saved" && + test_might_fail git prune --expire=now && + verbose git cat-file -e $bogus +' + +test_expect_success 'put bogus object into pack' ' + git tag reachable $bogus && + git repack -ad && + git tag -d reachable && + verbose git cat-file -e $bogus +' + +test_expect_success 'destructive repack keeps packed object' ' + test_might_fail git repack -Ad --unpack-unreachable=now && + verbose git cat-file -e $bogus && + test_might_fail git repack -ad && + verbose git cat-file -e $bogus +' + +# subsequent tests will have different corruptions +test_expect_success 'clean up bogus ref' ' + rm .git/refs/heads/bogus..name +' + +# We create two new objects here, "one" and "two". Our +# master branch points to "two", which is deleted, +# corrupting the repository. But we'd like to make sure +# that the otherwise unreachable "one" is not pruned +# (since it is the user's best bet for recovering +# from the corruption). +# +# Note that we also point HEAD somewhere besides "two", +# as we want to make sure we test the case where we +# pick up the reference to "two" by iterating the refs, +# not by resolving HEAD. +test_expect_success 'create history with missing tip commit' ' + test_tick && git commit --allow-empty -m one && + recoverable=$(git rev-parse HEAD) && + git cat-file commit $recoverable >saved && + test_tick && git commit --allow-empty -m two && + missing=$(git rev-parse HEAD) && + git checkout --detach $base && + rm .git/objects/$(echo $missing | sed "s,..,&/,") && + test_must_fail git cat-file -e $missing +' + +test_expect_success 'pruning with a corrupted tip does not drop history' ' + test_when_finished "git hash-object -w -t commit saved" && + test_might_fail git prune --expire=now && + verbose git cat-file -e $recoverable +' + +test_expect_success 'pack-refs does not silently delete broken loose ref' ' + git pack-refs --all --prune && + echo $missing >expect && + git rev-parse refs/heads/master >actual && + test_cmp expect actual +' + +# we do not want to count on running pack-refs to +# actually pack it, as it is perfectly reasonable to +# skip processing a broken ref +test_expect_success 'create packed-refs file with broken ref' ' + rm -f .git/refs/heads/master && + cat >.git/packed-refs <<-EOF && + $missing refs/heads/master + $recoverable refs/heads/other + EOF + echo $missing >expect && + git rev-parse refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'pack-refs does not silently delete broken packed ref' ' + git pack-refs --all --prune && + git rev-parse refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'pack-refs does not drop broken refs during deletion' ' + git update-ref -d refs/heads/other && + git rev-parse refs/heads/master >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 5b2b1c2c13..bd37f040b6 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -541,13 +541,30 @@ check_prot_path () { test_cmp expected actual } -check_prot_host_path () { - cat >expected <<-EOF && +check_prot_host_port_path () { + local diagport + case "$2" in + *ssh*) + pp=ssh + uah=userandhost + ehost=$(echo $3 | tr -d "[]") + diagport="Diag: port=$4" + ;; + *) + pp=$p + uah=hostandport + ehost=$(echo $3$4 | sed -e "s/22$/:22/" -e "s/NONE//") + diagport="" + ;; + esac + cat >exp <<-EOF && Diag: url=$1 - Diag: protocol=$2 - Diag: hostandport=$3 - Diag: path=$4 + Diag: protocol=$pp + Diag: $uah=$ehost + $diagport + Diag: path=$5 EOF + grep -v "^$" exp >expected git fetch-pack --diag-url "$1" >actual && test_cmp expected actual } @@ -557,22 +574,20 @@ do # git or ssh with scheme for p in "ssh+git" "git+ssh" git ssh do - for h in host host:12 [::1] [::1]:23 + for h in host user@host user@[::1] user@::1 do - case "$p" in - *ssh*) - pp=ssh - ;; - *) - pp=$p - ;; - esac test_expect_success "fetch-pack --diag-url $p://$h/$r" ' - check_prot_host_path $p://$h/$r $pp "$h" "/$r" + check_prot_host_port_path $p://$h/$r $p "$h" NONE "/$r" ' # "/~" -> "~" conversion test_expect_success "fetch-pack --diag-url $p://$h/~$r" ' - check_prot_host_path $p://$h/~$r $pp "$h" "~$r" + check_prot_host_port_path $p://$h/~$r $p "$h" NONE "~$r" + ' + done + for h in host User@host User@[::1] + do + test_expect_success "fetch-pack --diag-url $p://$h:22/$r" ' + check_prot_host_port_path $p://$h:22/$r $p "$h" 22 "/$r" ' done done @@ -603,11 +618,11 @@ do for h in host [::1] do test_expect_success "fetch-pack --diag-url $h:$r" ' - check_prot_path $h:$r $p "$r" + check_prot_host_port_path $h:$r $p "$h" NONE "$r" ' # Do "/~" -> "~" conversion test_expect_success "fetch-pack --diag-url $h:/~$r" ' - check_prot_host_path $h:/~$r $p "$h" "~$r" + check_prot_host_port_path $h:/~$r $p "$h" NONE "~$r" ' done done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 594d7a6998..050877f181 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1107,9 +1107,16 @@ test_expect_success 'fetch exact SHA1' ' git config uploadpack.allowtipsha1inwant true ) && - git fetch -v ../testrepo $the_commit:refs/heads/copy && - result=$(git rev-parse --verify refs/heads/copy) && - test "$the_commit" = "$result" + git fetch -v ../testrepo $the_commit:refs/heads/copy master:refs/heads/extra && + cat >expect <<-EOF && + $the_commit + $the_first_commit + EOF + { + git rev-parse --verify refs/heads/copy && + git rev-parse --verify refs/heads/extra + } >actual && + test_cmp expect actual ) ' diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 6cbc12d9a7..b97077351d 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -213,6 +213,17 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set test_cmp expect_cookies.txt cookies_tail.txt ' +test_expect_success 'transfer.hiderefs works over smart-http' ' + test_commit hidden && + test_commit visible && + git push public HEAD^:refs/heads/a HEAD:refs/heads/b && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ + config transfer.hiderefs refs/heads/a && + git clone --bare "$HTTPD_URL/smart/repo.git" hidden.git && + test_must_fail git -C hidden.git rev-parse --verify a && + git -C hidden.git rev-parse --verify b +' + test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' ( cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index e4f10c0f68..02b40b117f 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -301,11 +301,17 @@ expect_ssh () { (cd "$TRASH_DIRECTORY" && rm -f ssh-expect && >ssh-output) ' && { - case "$1" in - none) + case "$#" in + 1) ;; - *) + 2) echo "ssh: $1 git-upload-pack '$2'" + ;; + 3) + echo "ssh: $1 $2 git-upload-pack '$3'" + ;; + *) + echo "ssh: $1 $2 git-upload-pack '$3' $4" esac } >"$TRASH_DIRECTORY/ssh-expect" && (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) @@ -326,7 +332,7 @@ test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' ' test_expect_success 'bracketed hostnames are still ssh' ' git clone "[myhost:123]:src" ssh-bracket-clone && - expect_ssh myhost:123 src + expect_ssh myhost '-p 123' src ' counter=0 @@ -336,7 +342,8 @@ counter=0 test_clone_url () { counter=$(($counter + 1)) test_might_fail git clone "$1" tmp$counter && - expect_ssh "$2" "$3" + shift && + expect_ssh "$@" } test_expect_success !MINGW 'clone c:temp is ssl' ' @@ -359,7 +366,7 @@ done for repo in rep rep/home/project 123 do test_expect_success "clone [::1]:$repo" ' - test_clone_url [::1]:$repo ::1 $repo + test_clone_url [::1]:$repo ::1 "$repo" ' done #home directory @@ -400,24 +407,40 @@ test_expect_success 'clone ssh://host.xz:22/~repo' ' ' #IPv6 -test_expect_success 'clone ssh://[::1]/home/user/repo' ' - test_clone_url "ssh://[::1]/home/user/repo" "::1" "/home/user/repo" -' +for tuah in ::1 [::1] user@::1 user@[::1] [user@::1] +do + ehost=$(echo $tuah | tr -d "[]") + test_expect_success "clone ssh://$tuah/home/user/repo" " + test_clone_url ssh://$tuah/home/user/repo $ehost /home/user/repo + " +done #IPv6 from home directory -test_expect_success 'clone ssh://[::1]/~repo' ' - test_clone_url "ssh://[::1]/~repo" "::1" "~repo" -' +for tuah in ::1 [::1] user@::1 user@[::1] [user@::1] +do + euah=$(echo $tuah | tr -d "[]") + test_expect_success "clone ssh://$tuah/~repo" " + test_clone_url ssh://$tuah/~repo $euah '~repo' + " +done #IPv6 with port number -test_expect_success 'clone ssh://[::1]:22/home/user/repo' ' - test_clone_url "ssh://[::1]:22/home/user/repo" "-p 22 ::1" "/home/user/repo" -' +for tuah in [::1] user@[::1] [user@::1] +do + euah=$(echo $tuah | tr -d "[]") + test_expect_success "clone ssh://$tuah:22/home/user/repo" " + test_clone_url ssh://$tuah:22/home/user/repo '-p 22' $euah /home/user/repo + " +done #IPv6 from home directory with port number -test_expect_success 'clone ssh://[::1]:22/~repo' ' - test_clone_url "ssh://[::1]:22/~repo" "-p 22 ::1" "~repo" -' +for tuah in [::1] user@[::1] [user@::1] +do + euah=$(echo $tuah | tr -d "[]") + test_expect_success "clone ssh://$tuah:22/~repo" " + test_clone_url ssh://$tuah:22/~repo '-p 22' $euah '~repo' + " +done test_expect_success 'clone from a repository with two identical branches' ' diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 474dab381a..3cef18cfda 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -86,8 +86,8 @@ test_expect_success GPG 'show signed commit with signature' ' git show -s --show-signature initial >show && git verify-commit -v initial >verify.1 2>verify.2 && git cat-file commit initial >cat && - grep -v "gpg: " show >show.commit && - grep "gpg: " show >show.gpg && + grep -v -e "gpg: " -e "Warning: " show >show.commit && + grep -e "gpg: " -e "Warning: " show >show.gpg && grep -v "^ " cat | grep -v "^gpgsig " >cat.commit && test_cmp show.commit commit && test_cmp show.gpg verify.2 && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index f10a75290e..7a883d1a67 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -351,6 +351,25 @@ test_expect_success '__gitcomp_nl - doesnt fail because of invalid variable name __gitcomp_nl "$invalid_variable_name" ' +test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from config file' ' + cat >expect <<-EOF && + remote_from_file_1 + remote_from_file_2 + remote_in_config_1 + remote_in_config_2 + EOF + test_when_finished "rm -rf .git/remotes" && + mkdir -p .git/remotes && + >.git/remotes/remote_from_file_1 && + >.git/remotes/remote_from_file_2 && + test_when_finished "git remote remove remote_in_config_1" && + git remote add remote_in_config_1 git://remote_1 && + test_when_finished "git remote remove remote_in_config_2" && + git remote add remote_in_config_2 git://remote_2 && + __git_remotes >actual && + test_cmp expect actual +' + test_expect_success 'basic' ' run_completion "git " && # built-in diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index 51ecd3e4c1..46d7d37a51 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -397,6 +397,17 @@ test_expect_success 'prompt - untracked files status indicator - untracked files test_cmp expected "$actual" ' +test_expect_success 'prompt - untracked files status indicator - untracked files outside cwd' ' + printf " (master %%)" >expected && + ( + mkdir -p ignored_dir && + cd ignored_dir && + GIT_PS1_SHOWUNTRACKEDFILES=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' ' printf " (master)" >expected && test_config bash.showUntrackedFiles false && diff --git a/transport.c b/transport.c index 08bcd3a4eb..88bde1d85e 100644 --- a/transport.c +++ b/transport.c @@ -117,7 +117,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) return; } - if (hexval(buffer[0]) > 0xf) + if (!isxdigit(buffer[0])) continue; len = strlen(buffer); if (len && buffer[len - 1] == '\n') @@ -519,7 +519,7 @@ static int fetch_refs_via_pack(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct git_transport_data *data = transport->data; - const struct ref *refs; + struct ref *refs; char *dest = xstrdup(transport->url); struct fetch_pack_args args; struct ref *refs_tmp = NULL; @@ -552,15 +552,17 @@ 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)) + if (finish_connect(data->conn)) { + free_refs(refs); refs = NULL; + } data->conn = NULL; data->got_remote_heads = 0; data->options.self_contained_and_connected = args.self_contained_and_connected; free_refs(refs_tmp); - + free_refs(refs); free(dest); return (refs ? 0 : -1); } diff --git a/upload-pack.c b/upload-pack.c index b531a325d2..aa84576500 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -681,7 +681,7 @@ static void receive_needs(void) } /* return non-zero if the ref is hidden, otherwise 0 */ -static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +static int mark_our_ref(const char *refname, const unsigned char *sha1) { struct object *o = lookup_unknown_object(sha1); @@ -689,12 +689,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag o->flags |= HIDDEN_REF; return 1; } - if (!o) - die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1)); o->flags |= OUR_REF; return 0; } +static int check_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + mark_our_ref(refname, sha1); + return 0; +} + static void format_symref_info(struct strbuf *buf, struct string_list *symref) { struct string_list_item *item; @@ -713,7 +717,7 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo const char *refname_nons = strip_namespace(refname); unsigned char peeled[20]; - if (mark_our_ref(refname, sha1, flag, NULL)) + if (mark_our_ref(refname, sha1)) return 0; if (capabilities) { @@ -767,8 +771,8 @@ static void upload_pack(void) advertise_shallow_grafts(1); packet_flush(1); } else { - head_ref_namespaced(mark_our_ref, NULL); - for_each_namespaced_ref(mark_our_ref, NULL); + head_ref_namespaced(check_ref, NULL); + for_each_namespaced_ref(check_ref, NULL); } string_list_clear(&symref, 1); if (advertise_refs) @@ -159,6 +159,7 @@ void git_deflate_init(git_zstream *strm, int level) { int status; + memset(strm, 0, sizeof(*strm)); zlib_pre_call(strm); status = deflateInit(&strm->z, level); zlib_post_call(strm); @@ -172,6 +173,7 @@ static void do_git_deflate_init(git_zstream *strm, int level, int windowBits) { int status; + memset(strm, 0, sizeof(*strm)); zlib_pre_call(strm); status = deflateInit2(&strm->z, level, Z_DEFLATED, windowBits, |