diff options
110 files changed, 1429 insertions, 495 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index 4f13a23893..91a12c7e51 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -53,6 +53,7 @@ SP_ARTICLES += howto/setup-git-server-over-http SP_ARTICLES += howto/separating-topic-branches SP_ARTICLES += howto/revert-a-faulty-merge SP_ARTICLES += howto/recover-corrupted-blob-object +SP_ARTICLES += howto/recover-corrupted-object-harder SP_ARTICLES += howto/rebuild-from-update-hook SP_ARTICLES += howto/rebase-from-internal-branch SP_ARTICLES += howto/maintain-git diff --git a/Documentation/RelNotes/1.8.4.2.txt b/Documentation/RelNotes/1.8.4.2.txt index 867ae69070..9adccb1efb 100644 --- a/Documentation/RelNotes/1.8.4.2.txt +++ b/Documentation/RelNotes/1.8.4.2.txt @@ -4,6 +4,20 @@ Git v1.8.4.2 Release Notes Fixes since v1.8.4.1 -------------------- + * "git clone" gave some progress messages to the standard output, not + to the standard error, and did not allow suppressing them with the + "--no-progress" option. + + * "format-patch --from=<whom>" forgot to omit unnecessary in-body + from line, i.e. when <whom> is the same as the real author. + + * "git shortlog" used to choke and die when there is a malformed + commit (e.g. missing authors); it now simply ignore such a commit + and keeps going. + + * "git merge-recursive" did not parse its "--diff-algorithm=" command + line option correctly. + * "git branch --track" had a minor regression in v1.8.3.2 and later that made it impossible to base your local work on anything but a local branch of the upstream repository you are tracking from. diff --git a/Documentation/RelNotes/1.8.5.txt b/Documentation/RelNotes/1.8.5.txt index 4c2aa7077b..797e67bb73 100644 --- a/Documentation/RelNotes/1.8.5.txt +++ b/Documentation/RelNotes/1.8.5.txt @@ -76,6 +76,9 @@ Foreign interfaces, subsystems and ports. UI, Workflows & Features + * xdg-open can be used as a browser backend for "git web-browse" + (hence to show "git help -w" output), when available. + * "git grep" and "git show" pays attention to "--textconv" option when these commands are told to operate on blob objects (e.g. "git grep -e pattern HEAD:Makefile"). @@ -132,10 +135,6 @@ UI, Workflows & Features sync with the branch it builds on, and a branch that is configured to build on some other branch that no longer exists. - * A packfile that stores the same object more than once is broken and - will be rejected by "git index-pack" that is run when receiving - data over the wire. - * Earlier we started rejecting an attempt to add 0{40} object name to the index and to tree objects, but it sometimes is necessary to allow so to be able to use tools like filter-branch to correct such @@ -153,8 +152,9 @@ UI, Workflows & Features * "git push --no-thin" actually disables the "thin pack transfer" optimization. - * Magic pathspecs like ":(icase)makefile" that matches both - Makefile and makefile can be used in more places. + * Magic pathspecs like ":(icase)makefile" that matches both Makefile + and makefile and ":(glob)foo/**/bar" that matches "bar" in "foo" + and any subdirectory of "foo" can be used in more places. * The "http.*" variables can now be specified per URL that the configuration applies. For example, @@ -205,6 +205,12 @@ UI, Workflows & Features Performance, Internal Implementation, etc. + * "git for-each-ref" when asking for merely the object name does not + have to parse the object pointed at by the refs; the codepath has + been optimized. + + * The HTTP transport will try to use TCP keepalive when able. + * "git repack" is now written in C. * Build procedure for MSVC has been updated. @@ -242,6 +248,51 @@ Unless otherwise noted, all the fixes since v1.8.4 in the maintenance track are contained in this release (see release notes to them for details). + * A fast-import stream expresses a pathname with funny characters by + quoting them in C style; remote-hg remote helper forgot to unquote + such a path. + (merge 1136265 ap/remote-hg-unquote-cquote later to maint). + + * "git reset -p HEAD" has a codepath to special case it to behave + differently from resetting to contents of other commits, but a + recent change broke it. + + * Coloring around octopus merges in "log --graph" output was screwy. + (merge 339c17b hn/log-graph-color-octopus later to maint). + + * "git checkout topic", when there is not yet a local "topic" branch + but there is a unique remote-tracking branch for a remote "topic" + branch, pretended as if "git checkout -t -b topic remote/$r/topic" + (for that unique remote $r) was run. This hack however was not + implemented for "git checkout topic --". + (merge bca3969 mm/checkout-auto-track-fix later to maint). + + * One long-standing flaw in the pack transfer protocol used by "git + clone" was that there was no way to tell the other end which branch + "HEAD" points at, and the receiving end needed to guess. A new + capability has been defined in the pack protocol to convey this + information so that cloning from a repository with more than one + branches pointing at the same commit where the HEAD is at now + reliably sets the initial branch in the resulting repository. + (merge 360a326 jc/upload-pack-send-symref later to maint). + + * We did not handle cases where http transport gets redirected during + the authorization request (e.g. from http:// to https://). + (merge 70900ed jk/http-auth-redirects later to maint). + + * Bash prompting code to deal with an SVN remote as an upstream + were coded in a way not supported by older Bash versions (3.x). + (merge 52ec889 sg/prompt-svn-remote-fix later to maint). + + * The fall-back parsing of commit objects with broken author or + committer lines were less robust than ideal in picking up the + timestamps. + (merge 03818a4 jk/split-broken-ident later to maint). + + * "git rev-list --objects ^v1.0^ v1.0" gave v1.0 tag itself in the + output, but "git rev-list --objects v1.0^..v1.0" did not. + (merge 895c5ba jc/revision-range-unpeel later to maint). + * "git clone" gave some progress messages to the standard output, not to the standard error, and did not allow suppressing them with the --no-progress option. diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 4c1aff65e6..509cf73e50 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -14,7 +14,7 @@ SYNOPSIS DESCRIPTION ----------- -This script is used to combine all objects that do not currently +This command is used to combine all objects that do not currently reside in a "pack", into a pack. It can also be used to re-organize existing packs into a single, more efficient pack. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 2a3847649d..dcad890616 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -175,11 +175,11 @@ Skip "branches" and "tags" of first level directories;; precedence over '--include-paths'. --log-window-size=<n>;; - Fetch <n> log entries per request when scanning Subversion history. - The default is 100. For very large Subversion repositories, larger - values may be needed for 'clone'/'fetch' to complete in reasonable - time. But overly large values may lead to higher memory usage and - request timeouts. + Fetch <n> log entries per request when scanning Subversion history. + The default is 100. For very large Subversion repositories, larger + values may be needed for 'clone'/'fetch' to complete in reasonable + time. But overly large values may lead to higher memory usage and + request timeouts. 'clone':: Runs 'init' and 'fetch'. It will automatically create a @@ -275,7 +275,7 @@ first have already been pushed into SVN. For each patch, one may answer "yes" (accept this patch), "no" (discard this patch), "all" (accept all patches), or "quit". + - 'git svn dcommit' returns immediately if answer is "no" or "quit", without + 'git svn dcommit' returns immediately if answer is "no" or "quit", without committing anything to SVN. 'branch':: @@ -366,12 +366,12 @@ environment). This command has the same behaviour. Any other arguments are passed directly to 'git log' 'blame':: - Show what revision and author last modified each line of a file. The - output of this mode is format-compatible with the output of - `svn blame' by default. Like the SVN blame command, - local uncommitted changes in the working tree are ignored; - the version of the file in the HEAD revision is annotated. Unknown - arguments are passed directly to 'git blame'. + Show what revision and author last modified each line of a file. The + output of this mode is format-compatible with the output of + `svn blame' by default. Like the SVN blame command, + local uncommitted changes in the working tree are ignored; + the version of the file in the HEAD revision is annotated. Unknown + arguments are passed directly to 'git blame'. + --git-format;; Produce output in the same format as 'git blame', but with @@ -830,7 +830,7 @@ Tracking and contributing to an entire Subversion-managed project # View all branches and tags you have cloned: git branch -r # Create a new branch in SVN - git svn branch waldo + git svn branch waldo # Reset your master to trunk (or any other branch, replacing 'trunk' # with the appropriate name): git reset --hard svn/trunk diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt index ff23494e70..12cb108b85 100644 --- a/Documentation/git-unpack-objects.txt +++ b/Documentation/git-unpack-objects.txt @@ -9,7 +9,7 @@ git-unpack-objects - Unpack objects from a packed archive SYNOPSIS -------- [verse] -'git unpack-objects' [-n] [-q] [-r] [--strict] <pack-file +'git unpack-objects' [-n] [-q] [-r] [--strict] < <pack-file> DESCRIPTION diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt index 5aec4ecffb..2de575f5be 100644 --- a/Documentation/git-web--browse.txt +++ b/Documentation/git-web--browse.txt @@ -35,6 +35,7 @@ The following browsers (or commands) are currently supported: * open (this is the default under Mac OS X GUI) * start (this is the default under MinGW) * cygstart (this is the default under Cygwin) +* xdg-open Custom commands may also be specified. diff --git a/Documentation/git.txt b/Documentation/git.txt index 4f7e07f2e0..824a179a96 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.8.4.1/git.html[documentation for release 1.8.4.1] +* link:v1.8.4.2/git.html[documentation for release 1.8.4.2] * release notes for + link:RelNotes/1.8.4.2.txt[1.8.4.2], link:RelNotes/1.8.4.1.txt[1.8.4.1], link:RelNotes/1.8.4.txt[1.8.4]. diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index c17e760184..d44e14c138 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -8,7 +8,7 @@ gitk - The Git repository browser SYNOPSIS -------- [verse] -'gitk' [<option>...] [<revs>] [--] [<path>...] +'gitk' [<options>] [<revision range>] [\--] [<path>...] DESCRIPTION ----------- @@ -16,21 +16,38 @@ Displays changes in a repository or a selected set of commits. This includes visualizing the commit graph, showing information related to each commit, and the files in the trees of each revision. -Historically, gitk was the first repository browser. It's written in tcl/tk -and started off in a separate repository but was later merged into the main -Git repository. - OPTIONS ------- -To control which revisions to show, the command takes options applicable to -the 'git rev-list' command (see linkgit:git-rev-list[1]). -This manual page describes only the most -frequently used options. --n <number>:: ---max-count=<number>:: +To control which revisions to show, gitk supports most options +applicable to the 'git rev-list' command. It also supports a few +options applicable to the 'git diff-*' commands to control how the +changes each commit introduces are shown. Finally, it supports some +gitk-specific options. + +gitk generally only understands options with arguments in the +'sticked' form (see linkgit:gitcli[7]) due to limitations in the +command line parser. + +rev-list options and arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This manual page describes only the most frequently used options. See +linkgit:git-rev-list[1] for a complete list. + +--all:: + + Show all refs (branches, tags, etc.). - Limits the number of commits to show. +--branches[=<pattern>]:: +--tags[=<pattern>]:: +--remotes[=<pattern>]:: + + Pretend as if all the branches (tags, remote branches, resp.) + are listed on the command line as '<commit>'. If '<pattern>' + is given, limit refs to ones matching given shell glob. If + pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the + end is implied. --since=<date>:: @@ -40,9 +57,9 @@ frequently used options. Show commits older than a specific date. ---all:: +--date-order:: - Show all branches. + Sort commits by date when possible. --merge:: @@ -51,19 +68,37 @@ frequently used options. that modify the conflicted files and do not exist on all the heads being merged. ---argscmd=<command>:: - Command to be run each time gitk has to determine the list of - <revs> to show. The command is expected to print on its standard - output a list of additional revs to be shown, one per line. - Use this instead of explicitly specifying <revs> if the set of - commits to show may vary between refreshes. +--left-right:: ---select-commit=<ref>:: + Mark which side of a symmetric diff a commit is reachable + from. Commits from the left side are prefixed with a `<` + symbol and those from the right with a `>` symbol. - Automatically select the specified commit after loading the graph. - Default behavior is equivalent to specifying '--select-commit=HEAD'. +--full-history:: + + When filtering history with '<path>...', does not prune some + history. (See "History simplification" in linkgit:git-log[1] + for a more detailed explanation.) + +--simplify-merges:: -<revs>:: + Additional option to '--full-history' to remove some needless + merges from the resulting history, as there are no selected + commits contributing to this merge. (See "History + simplification" in linkgit:git-log[1] for a more detailed + explanation.) + +--ancestry-path:: + + When given a range of commits to display + (e.g. 'commit1..commit2' or 'commit2 {caret}commit1'), only + display commits that exist directly on the ancestry chain + between the 'commit1' and 'commit2', i.e. commits that are + both descendants of 'commit1', and ancestors of 'commit2'. + (See "History simplification" in linkgit:git-log[1] for a more + detailed explanation.) + +<revision range>:: Limit the revisions to show. This can be either a single revision meaning show from the given revision and back, or it can be a range in @@ -78,6 +113,23 @@ frequently used options. avoid ambiguity with respect to revision names use "--" to separate the paths from any preceding options. +gitk-specific options +~~~~~~~~~~~~~~~~~~~~~ + +--argscmd=<command>:: + + Command to be run each time gitk has to determine the revision + range to show. The command is expected to print on its + standard output a list of additional revisions to be shown, + one per line. Use this instead of explicitly specifying a + '<revision range>' if the set of commits to show may vary + between refreshes. + +--select-commit=<ref>:: + + Select the specified commit after loading the graph. + Default behavior is equivalent to specifying '--select-commit=HEAD'. + Examples -------- gitk v2.6.12.. include/scsi drivers/scsi:: @@ -101,6 +153,13 @@ Files Gitk creates the .gitk file in your $HOME directory to store preferences such as display options, font, and colors. +History +------- +Gitk was the first graphical repository browser. It's written in +tcl/tk and started off in a separate repository but was later merged +into the main Git repository. + + SEE ALSO -------- 'qgit(1)':: diff --git a/Documentation/howto/recover-corrupted-object-harder.txt b/Documentation/howto/recover-corrupted-object-harder.txt new file mode 100644 index 0000000000..6f33dac0e0 --- /dev/null +++ b/Documentation/howto/recover-corrupted-object-harder.txt @@ -0,0 +1,242 @@ +Date: Wed, 16 Oct 2013 04:34:01 -0400 +From: Jeff King <peff@peff.net> +Subject: pack corruption post-mortem +Abstract: Recovering a corrupted object when no good copy is available. +Content-type: text/asciidoc + +How to recover an object from scratch +===================================== + +I was recently presented with a repository with a corrupted packfile, +and was asked if the data was recoverable. This post-mortem describes +the steps I took to investigate and fix the problem. I thought others +might find the process interesting, and it might help somebody in the +same situation. + +******************************** +Note: In this case, no good copy of the repository was available. For +the much easier case where you can get the corrupted object from +elsewhere, see link:recover-corrupted-blob-object.html[this howto]. +******************************** + +I started with an fsck, which found a problem with exactly one object +(I've used $pack and $obj below to keep the output readable, and also +because I'll refer to them later): + +----------- + $ git fsck + error: $pack SHA1 checksum mismatch + error: index CRC mismatch for object $obj from $pack at offset 51653873 + error: inflate: data stream error (incorrect data check) + error: cannot unpack $obj from $pack at offset 51653873 +----------- + +The pack checksum failing means a byte is munged somewhere, and it is +presumably in the object mentioned (since both the index checksum and +zlib were failing). + +Reading the zlib source code, I found that "incorrect data check" means +that the adler-32 checksum at the end of the zlib data did not match the +inflated data. So stepping the data through zlib would not help, as it +did not fail until the very end, when we realize the crc does not match. +The problematic bytes could be anywhere in the object data. + +The first thing I did was pull the broken data out of the packfile. I +needed to know how big the object was, which I found out with: + +------------ + $ git show-index <$idx | cut -d' ' -f1 | sort -n | grep -A1 51653873 + 51653873 + 51664736 +------------ + +Show-index gives us the list of objects and their offsets. We throw away +everything but the offsets, and then sort them so that our interesting +offset (which we got from the fsck output above) is followed immediately +by the offset of the next object. Now we know that the object data is +10863 bytes long, and we can grab it with: + +------------ + dd if=$pack of=object bs=1 skip=51653873 count=10863 +------------ + +I inspected a hexdump of the data, looking for any obvious bogosity +(e.g., a 4K run of zeroes would be a good sign of filesystem +corruption). But everything looked pretty reasonable. + +Note that the "object" file isn't fit for feeding straight to zlib; it +has the git packed object header, which is variable-length. We want to +strip that off so we can start playing with the zlib data directly. You +can either work your way through it manually (the format is described in +link:../technical/pack-format.html[Documentation/technical/pack-format.txt]), +or you can walk through it in a debugger. I did the latter, creating a +valid pack like: + +------------ + # pack magic and version + printf 'PACK\0\0\0\2' >tmp.pack + # pack has one object + printf '\0\0\0\1' >>tmp.pack + # now add our object data + cat object >>tmp.pack + # and then append the pack trailer + /path/to/git.git/test-sha1 -b <tmp.pack >trailer + cat trailer >>tmp.pack +------------ + +and then running "git index-pack tmp.pack" in the debugger (stop at +unpack_raw_entry). Doing this, I found that there were 3 bytes of header +(and the header itself had a sane type and size). So I stripped those +off with: + +------------ + dd if=object of=zlib bs=1 skip=3 +------------ + +I ran the result through zlib's inflate using a custom C program. And +while it did report the error, I did get the right number of output +bytes (i.e., it matched git's size header that we decoded above). But +feeding the result back to "git hash-object" didn't produce the same +sha1. So there were some wrong bytes, but I didn't know which. The file +happened to be C source code, so I hoped I could notice something +obviously wrong with it, but I didn't. I even got it to compile! + +I also tried comparing it to other versions of the same path in the +repository, hoping that there would be some part of the diff that didn't +make sense. Unfortunately, this happened to be the only revision of this +particular file in the repository, so I had nothing to compare against. + +So I took a different approach. Working under the guess that the +corruption was limited to a single byte, I wrote a program to munge each +byte individually, and try inflating the result. Since the object was +only 10K compressed, that worked out to about 2.5M attempts, which took +a few minutes. + +The program I used is here: + +---------------------------------------------- +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <zlib.h> + +static int try_zlib(unsigned char *buf, int len) +{ + /* make this absurdly large so we don't have to loop */ + static unsigned char out[1024*1024]; + z_stream z; + int ret; + + memset(&z, 0, sizeof(z)); + inflateInit(&z); + + z.next_in = buf; + z.avail_in = len; + z.next_out = out; + z.avail_out = sizeof(out); + + ret = inflate(&z, 0); + inflateEnd(&z); + return ret >= 0; +} + +/* eye candy */ +static int counter = 0; +static void progress(int sig) +{ + fprintf(stderr, "\r%d", counter); + alarm(1); +} + +int main(void) +{ + /* oversized so we can read the whole buffer in */ + unsigned char buf[1024*1024]; + int len; + unsigned i, j; + + signal(SIGALRM, progress); + alarm(1); + + len = read(0, buf, sizeof(buf)); + for (i = 0; i < len; i++) { + unsigned char c = buf[i]; + for (j = 0; j <= 0xff; j++) { + buf[i] = j; + + counter++; + if (try_zlib(buf, len)) + printf("i=%d, j=%x\n", i, j); + } + buf[i] = c; + } + + alarm(0); + fprintf(stderr, "\n"); + return 0; +} +---------------------------------------------- + +I compiled and ran with: + +------- + gcc -Wall -Werror -O3 munge.c -o munge -lz + ./munge <zlib +------- + + +There were a few false positives early on (if you write "no data" in the +zlib header, zlib thinks it's just fine :) ). But I got a hit about +halfway through: + +------- + i=5642, j=c7 +------- + +I let it run to completion, and got a few more hits at the end (where it +was munging the crc to match our broken data). So there was a good +chance this middle hit was the source of the problem. + +I confirmed by tweaking the byte in a hex editor, zlib inflating the +result (no errors!), and then piping the output into "git hash-object", +which reported the sha1 of the broken object. Success! + +I fixed the packfile itself with: + +------- + chmod +w $pack + printf '\xc7' | dd of=$pack bs=1 seek=51659518 conv=notrunc + chmod -w $pack +------- + +The `\xc7` comes from the replacement byte our "munge" program found. +The offset 51659518 is derived by taking the original object offset +(51653873), adding the replacement offset found by "munge" (5642), and +then adding back in the 3 bytes of git header we stripped. + +After that, "git fsck" ran clean. + +As for the corruption itself, I was lucky that it was indeed a single +byte. In fact, it turned out to be a single bit. The byte 0xc7 was +corrupted to 0xc5. So presumably it was caused by faulty hardware, or a +cosmic ray. + +And the aborted attempt to look at the inflated output to see what was +wrong? I could have looked forever and never found it. Here's the diff +between what the corrupted data inflates to, versus the real data: + +-------------- + - cp = strtok (arg, "+"); + + cp = strtok (arg, "."); +-------------- + +It tweaked one byte and still ended up as valid, readable C that just +happened to do something totally different! One takeaway is that on a +less unlucky day, looking at the zlib output might have actually been +helpful, as most random changes would actually break the C code. + +But more importantly, git's hashing and checksumming noticed a problem +that easily could have gone undetected in another system. The result +still compiled, but would have caused an interesting bug (that would +have been blamed on some random commit). diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt index 981cbddc86..6de4f3c487 100644 --- a/Documentation/howto/setup-git-server-over-http.txt +++ b/Documentation/howto/setup-git-server-over-http.txt @@ -6,6 +6,10 @@ Content-type: text/asciidoc How to setup Git server over http ================================= +NOTE: This document is from 2006. A lot has happened since then, and this +document is now relevant mainly if your web host is not CGI capable. +Almost everyone else should instead look at linkgit:git-http-backend[1]. + Since Apache is one of those packages people like to compile themselves while others prefer the bureaucrat's dream Debian, it is impossible to give guidelines which will work for everyone. Just send diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index b444c18f17..4dcaac3e97 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.8.4.GIT +DEF_VER=v1.8.5-rc0 LF=' ' @@ -110,7 +110,7 @@ static const char *real_path_internal(const char *path, int die_on_error) else goto error_out; } - if (len && !is_dir_sep(buf[len-1])) + if (len && !is_dir_sep(buf[len - 1])) buf[len++] = '/'; strcpy(buf + len, last_elem); free(last_elem); @@ -201,7 +201,7 @@ const char *absolute_path(const char *path) if (!cwd) die_errno("Cannot determine the current working directory"); len = strlen(cwd); - fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s"; + fmt = (len > 0 && is_dir_sep(cwd[len - 1])) ? "%s%s" : "%s/%s"; if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX) die("Too long path: %.*s", 60, path); } @@ -5,7 +5,7 @@ static char *alias_val; static int alias_lookup_cb(const char *k, const char *v, void *cb) { - if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) { + if (!prefixcmp(k, "alias.") && !strcmp(k + 6, alias_key)) { if (!v) return config_error_nonbool(k); alias_val = xstrdup(v); @@ -34,7 +34,7 @@ int split_cmdline(char *cmdline, const char ***argv) int src, dst, count = 0, size = 16; char quoted = 0; - *argv = xmalloc(sizeof(char *) * size); + *argv = xmalloc(sizeof(**argv) * size); /* split alias_string */ (*argv)[count++] = cmdline; @@ -45,7 +45,7 @@ int split_cmdline(char *cmdline, const char ***argv) while (cmdline[++src] && isspace(cmdline[src])) ; /* skip */ - ALLOC_GROW(*argv, count+1, size); + ALLOC_GROW(*argv, count + 1, size); (*argv)[count++] = cmdline + dst; } else if (!quoted && (c == '\'' || c == '"')) { quoted = c; @@ -76,12 +76,13 @@ int split_cmdline(char *cmdline, const char ***argv) return -SPLIT_CMDLINE_UNCLOSED_QUOTE; } - ALLOC_GROW(*argv, count+1, size); + ALLOC_GROW(*argv, count + 1, size); (*argv)[count] = NULL; return count; } -const char *split_cmdline_strerror(int split_cmdline_errno) { - return split_cmdline_errors[-split_cmdline_errno-1]; +const char *split_cmdline_strerror(int split_cmdline_errno) +{ + return split_cmdline_errors[-split_cmdline_errno - 1]; } @@ -58,7 +58,7 @@ static void report(const char *name, unsigned int count, size_t size) } #define REPORT(name) \ - report(#name, name##_allocs, name##_allocs*sizeof(struct name) >> 10) + report(#name, name##_allocs, name##_allocs * sizeof(struct name) >> 10) void alloc_report(void) { @@ -440,7 +440,7 @@ static int match_extension(const char *filename, const char *ext) * prefix is non-empty (k.e., we don't match .tar.gz with no actual * filename). */ - if (prefixlen < 2 || filename[prefixlen-1] != '.') + if (prefixlen < 2 || filename[prefixlen - 1] != '.') return 0; return !strcmp(filename + prefixlen, ext); } @@ -41,7 +41,7 @@ int decode_85(char *dst, const char *buffer, int len) { prep_base85(); - say2("decode 85 <%.*s>", len/4*5, buffer); + say2("decode 85 <%.*s>", len / 4 * 5, buffer); while (len) { unsigned acc = 0; int de, cnt = 4; diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index a8d4bf9301..e1a1eb6097 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -274,10 +274,10 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) padlen[1] = htonl((uint32_t)(ctx->size << 3)); i = ctx->size & 63; - blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); + blk_SHA1_Update(ctx, pad, 1 + (63 & (55 - i))); blk_SHA1_Update(ctx, padlen, 8); /* Output hash */ for (i = 0; i < 5; i++) - put_be32(hashout + i*4, ctx->H[i]); + put_be32(hashout + i * 4, ctx->H[i]); } diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 69e167b16c..61e75eb60c 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -14,7 +14,7 @@ static int line_termination = '\n'; static int checkout_stage; /* default to checkout stage0 */ static int to_tempfile; -static char topath[4][PATH_MAX + 1]; +static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static struct checkout state; diff --git a/builtin/checkout.c b/builtin/checkout.c index 0f57397037..54f80bd38a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -873,7 +873,9 @@ static int parse_branchname_arg(int argc, const char **argv, int argcount = 0; unsigned char branch_rev[20]; const char *arg; - int has_dash_dash; + int dash_dash_pos; + int has_dash_dash = 0; + int i; /* * case 1: git checkout <ref> -- [<paths>] @@ -885,20 +887,30 @@ static int parse_branchname_arg(int argc, const char **argv, * * everything after the '--' must be paths. * - * case 3: git checkout <something> [<paths>] + * case 3: git checkout <something> [--] * - * With no paths, if <something> is a commit, that is to - * switch to the branch or detach HEAD at it. As a special case, - * if <something> is A...B (missing A or B means HEAD but you can - * omit at most one side), and if there is a unique merge base - * between A and B, A...B names that merge base. + * (a) If <something> is a commit, that is to + * switch to the branch or detach HEAD at it. As a special case, + * if <something> is A...B (missing A or B means HEAD but you can + * omit at most one side), and if there is a unique merge base + * between A and B, A...B names that merge base. * - * With no paths, if <something> is _not_ a commit, no -t nor -b - * was given, and there is a tracking branch whose name is - * <something> in one and only one remote, then this is a short-hand - * to fork local <something> from that remote-tracking branch. + * (b) If <something> is _not_ a commit, either "--" is present + * or <something> is not a path, no -t nor -b was given, and + * and there is a tracking branch whose name is <something> + * in one and only one remote, then this is a short-hand to + * fork local <something> from that remote-tracking branch. * - * Otherwise <something> shall not be ambiguous. + * (c) Otherwise, if "--" is present, treat it like case (1). + * + * (d) Otherwise : + * - if it's a reference, treat it like case (1) + * - else if it's a path, treat it like case (2) + * - else: fail. + * + * case 4: git checkout <something> <paths> + * + * The first argument must not be ambiguous. * - If it's *only* a reference, treat it like case (1). * - If it's only a path, treat it like case (2). * - else: fail. @@ -907,28 +919,59 @@ static int parse_branchname_arg(int argc, const char **argv, if (!argc) return 0; - if (!strcmp(argv[0], "--")) /* case (2) */ - return 1; - arg = argv[0]; - has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + dash_dash_pos = -1; + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + dash_dash_pos = i; + break; + } + } + if (dash_dash_pos == 0) + return 1; /* case (2) */ + else if (dash_dash_pos == 1) + has_dash_dash = 1; /* case (3) or (1) */ + else if (dash_dash_pos >= 2) + die(_("only one reference expected, %d given."), dash_dash_pos); if (!strcmp(arg, "-")) arg = "@{-1}"; if (get_sha1_mb(arg, rev)) { - if (has_dash_dash) /* case (1) */ - die(_("invalid reference: %s"), arg); - if (dwim_new_local_branch_ok && - !check_filename(NULL, arg) && - argc == 1) { + /* + * Either case (3) or (4), with <something> not being + * a commit, or an attempt to use case (1) with an + * invalid ref. + * + * It's likely an error, but we need to find out if + * we should auto-create the branch, case (3).(b). + */ + int recover_with_dwim = dwim_new_local_branch_ok; + + if (check_filename(NULL, arg) && !has_dash_dash) + recover_with_dwim = 0; + /* + * Accept "git checkout foo" and "git checkout foo --" + * as candidates for dwim. + */ + if (!(argc == 1 && !has_dash_dash) && + !(argc == 2 && has_dash_dash)) + recover_with_dwim = 0; + + if (recover_with_dwim) { const char *remote = unique_tracking_name(arg, rev); - if (!remote) - return argcount; - *new_branch = arg; - arg = remote; - /* DWIMmed to create local branch */ - } else { + if (remote) { + *new_branch = arg; + arg = remote; + /* DWIMmed to create local branch, case (3).(b) */ + } else { + recover_with_dwim = 0; + } + } + + if (!recover_with_dwim) { + if (has_dash_dash) + die(_("invalid reference: %s"), arg); return argcount; } } @@ -958,7 +1001,7 @@ static int parse_branchname_arg(int argc, const char **argv, if (!*source_tree) /* case (1): want a tree */ die(_("reference is not a tree: %s"), arg); - if (!has_dash_dash) {/* case (3 -> 1) */ + if (!has_dash_dash) {/* case (3).(d) -> (1) */ /* * Do not complain the most common case * git checkout branch diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 1d4083c2dd..d096051b15 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -205,6 +205,22 @@ static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned lo return buf; } +static int grab_objectname(const char *name, const unsigned char *sha1, + struct atom_value *v) +{ + if (!strcmp(name, "objectname")) { + char *s = xmalloc(41); + strcpy(s, sha1_to_hex(sha1)); + v->s = s; + return 1; + } + if (!strcmp(name, "objectname:short")) { + v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); + return 1; + } + return 0; +} + /* See grab_values */ static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) { @@ -225,15 +241,8 @@ static void grab_common_values(struct atom_value *val, int deref, struct object v->ul = sz; v->s = s; } - else if (!strcmp(name, "objectname")) { - char *s = xmalloc(41); - strcpy(s, sha1_to_hex(obj->sha1)); - v->s = s; - } - else if (!strcmp(name, "objectname:short")) { - v->s = xstrdup(find_unique_abbrev(obj->sha1, - DEFAULT_ABBREV)); - } + else if (deref) + grab_objectname(name, obj->sha1, v); } } @@ -676,6 +685,8 @@ static void populate_value(struct refinfo *ref) } continue; } + else if (!deref && grab_objectname(name, ref->objectname, v)) + continue; else continue; diff --git a/builtin/gc.c b/builtin/gc.c index 891a2c2ecb..c14190f840 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -14,6 +14,7 @@ #include "cache.h" #include "parse-options.h" #include "run-command.h" +#include "sigchain.h" #include "argv-array.h" #define FAILED_RUN "failed to run %s" @@ -35,6 +36,21 @@ static struct argv_array repack = ARGV_ARRAY_INIT; static struct argv_array prune = ARGV_ARRAY_INIT; static struct argv_array rerere = ARGV_ARRAY_INIT; +static char *pidfile; + +static void remove_pidfile(void) +{ + if (pidfile) + unlink(pidfile); +} + +static void remove_pidfile_on_signal(int signo) +{ + remove_pidfile(); + sigchain_pop(signo); + raise(signo); +} + static int gc_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "gc.packrefs")) { @@ -179,6 +195,10 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) FILE *fp; int fd, should_exit; + if (pidfile) + /* already locked */ + return NULL; + if (gethostname(my_host, sizeof(my_host))) strcpy(my_host, "unknown"); @@ -219,6 +239,10 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) strbuf_release(&sb); commit_lock_file(&lock); + pidfile = git_pathdup("gc.pid"); + sigchain_push_common(remove_pidfile_on_signal); + atexit(remove_pidfile); + return NULL; } diff --git a/builtin/mv.c b/builtin/mv.c index aec79d1838..2e0e61b651 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -55,6 +55,7 @@ static const char *add_slash(const char *path) } static struct lock_file lock_file; +#define SUBMODULE_WITH_GITDIR ((const char *)1) int cmd_mv(int argc, const char **argv, const char *prefix) { @@ -132,6 +133,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); + else + submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); @@ -230,10 +233,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) die_errno (_("renaming '%s' failed"), src); - if (submodule_gitfile[i]) - connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); - if (!update_path_in_gitmodules(src, dst)) - gitmodules_modified = 1; + if (submodule_gitfile[i]) { + if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) + connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); + if (!update_path_in_gitmodules(src, dst)) + gitmodules_modified = 1; + } } if (mode == WORKING_DIRECTORY) diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 0f5d7fe23f..0d7ef847a7 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -178,7 +178,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) if (1 < opts.index_only + opts.update) die("-u and -i at the same time makes no sense"); - if ((opts.update||opts.index_only) && !opts.merge) + if ((opts.update || opts.index_only) && !opts.merge) die("%s is meaningless without -m, --reset, or --prefix", opts.update ? "-u" : "-i"); if ((opts.dir && !opts.update)) diff --git a/builtin/reset.c b/builtin/reset.c index f2f9d55392..60048030dd 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -304,7 +304,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); - return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", &pathspec); + return run_add_interactive(rev, "--patch=reset", &pathspec); } /* git reset tree [--] paths... can be used to diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 4fc1616637..0745e2d053 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -322,7 +322,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.commit_format = CMIT_FMT_RAW; if ((!revs.commits && - (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && + (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) && !revs.pending.nr)) || revs.diff) usage(rev_list_usage); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index f481959421..71286b4fae 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -47,7 +47,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_symbolic_ref_usage, 0); - if (msg &&!*msg) + if (msg && !*msg) die("Refusing to perform update with empty message"); if (delete) { @@ -398,7 +398,6 @@ extern int is_bare_repository(void); extern int is_inside_git_dir(void); extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); -extern int have_git_dir(void); extern const char *get_git_dir(void); extern int is_git_directory(const char *path); extern char *get_object_directory(void); @@ -751,6 +750,7 @@ int is_directory(const char *); const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); +const char *remove_leading_path(const char *in, const char *prefix); const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); int normalize_path_copy_len(char *dst, const char *src, int *prefix_len); int normalize_path_copy(char *dst, const char *src); @@ -975,6 +975,7 @@ struct checkout { refresh_cache:1; }; +#define TEMPORARY_FILENAME_LENGTH 25 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); struct cache_def { diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c index b2c5d465ac..06f3088708 100644 --- a/compat/regex/regcomp.c +++ b/compat/regex/regcomp.c @@ -339,7 +339,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, p = buf; *p++ = dfa->nodes[node].opr.c; while (++node < dfa->nodes_len - && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].type == CHARACTER && dfa->nodes[node].mb_partial) *p++ = dfa->nodes[node].opr.c; memset (&state, '\0', sizeof (state)); @@ -7,8 +7,10 @@ #include "remote.h" #include "connect.h" #include "url.h" +#include "string-list.h" static char *server_capabilities; +static const char *parse_feature_value(const char *, const char *, int *); static int check_ref(const char *name, int len, unsigned int flags) { @@ -60,6 +62,61 @@ static void die_initial_contact(int got_at_least_one_head) "and the repository exists."); } +static void parse_one_symref_info(struct string_list *symref, const char *val, int len) +{ + char *sym, *target; + struct string_list_item *item; + + if (!len) + return; /* just "symref" */ + /* e.g. "symref=HEAD:refs/heads/master" */ + sym = xmalloc(len + 1); + memcpy(sym, val, len); + sym[len] = '\0'; + target = strchr(sym, ':'); + if (!target) + /* just "symref=something" */ + goto reject; + *(target++) = '\0'; + if (check_refname_format(sym, REFNAME_ALLOW_ONELEVEL) || + check_refname_format(target, REFNAME_ALLOW_ONELEVEL)) + /* "symref=bogus:pair */ + goto reject; + item = string_list_append(symref, sym); + item->util = target; + return; +reject: + free(sym); + return; +} + +static void annotate_refs_with_symref_info(struct ref *ref) +{ + struct string_list symref = STRING_LIST_INIT_DUP; + const char *feature_list = server_capabilities; + + while (feature_list) { + int len; + const char *val; + + val = parse_feature_value(feature_list, "symref", &len); + if (!val) + break; + parse_one_symref_info(&symref, val, len); + feature_list = val + 1; + } + sort_string_list(&symref); + + for (; ref; ref = ref->next) { + struct string_list_item *item; + item = string_list_lookup(&symref, ref->name); + if (!item) + continue; + ref->symref = xstrdup((char *)item->util); + } + string_list_clear(&symref, 0); +} + /* * Read all the refs from the other end */ @@ -67,6 +124,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, struct ref **list, unsigned int flags, struct extra_have_objects *extra_have) { + struct ref **orig_list = list; int got_at_least_one_head = 0; *list = NULL; @@ -114,10 +172,13 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len, list = &ref->next; got_at_least_one_head = 1; } + + annotate_refs_with_symref_info(*orig_list); + return list; } -const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp) +static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp) { int len; @@ -8,6 +8,5 @@ extern int git_connection_is_socket(struct child_process *conn); extern int server_supports(const char *feature); extern int parse_feature_request(const char *features, const char *feature); extern const char *server_feature_value(const char *feature, int *len_ret); -extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret); #endif diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh index 202e2e520f..7b732d2aeb 100644 --- a/contrib/completion/git-prompt.sh +++ b/contrib/completion/git-prompt.sh @@ -111,7 +111,7 @@ __git_ps1_show_upstream () ;; svn-remote.*.url) svn_remote[$((${#svn_remote[@]} + 1))]="$value" - svn_url_pattern+="\\|$value" + svn_url_pattern="$svn_url_pattern\\|$value" upstream=svn+git # default upstream is SVN if available, else git ;; esac diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg index 92d994e470..3222afd9da 100755 --- a/contrib/remote-helpers/git-remote-hg +++ b/contrib/remote-helpers/git-remote-hg @@ -703,6 +703,11 @@ def get_merge_files(repo, p1, p2, files): f = { 'ctx' : repo[p1][e] } files[e] = f +def c_style_unescape(string): + if string[0] == string[-1] == '"': + return string.decode('string-escape')[1:-1] + return string + def parse_commit(parser): from_mark = merge_mark = None @@ -742,6 +747,7 @@ def parse_commit(parser): f = { 'deleted' : True } else: die('Unknown file command: %s' % line) + path = c_style_unescape(path).decode('utf-8') files[path] = f # only export the commits if we are on an internal proxy repo diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile index 435b2dea29..4030a16898 100644 --- a/contrib/subtree/Makefile +++ b/contrib/subtree/Makefile @@ -21,13 +21,14 @@ GIT_SUBTREE := git-subtree GIT_SUBTREE_DOC := git-subtree.1 GIT_SUBTREE_XML := git-subtree.xml GIT_SUBTREE_TXT := git-subtree.txt +GIT_SUBTREE_HTML := git-subtree.html all: $(GIT_SUBTREE) $(GIT_SUBTREE): $(GIT_SUBTREE_SH) cp $< $@ && chmod +x $@ -doc: $(GIT_SUBTREE_DOC) +doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML) install: $(GIT_SUBTREE) $(INSTALL) -d -m 755 $(DESTDIR)$(libexecdir) @@ -46,6 +47,10 @@ $(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT) asciidoc -b docbook -d manpage -f $(ASCIIDOC_CONF) \ -agit_version=$(gitver) $^ +$(GIT_SUBTREE_HTML): $(GIT_SUBTREE_TXT) + asciidoc -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \ + -agit_version=$(gitver) $^ + test: $(MAKE) -C t/ test @@ -907,7 +907,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm const char *end = date; int i; - while (isalpha(*++end)); + while (isalpha(*++end)) ; for (i = 0; i < 12; i++) { @@ -234,19 +234,30 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen) return lstat(path, st); } +/* + * Write the contents from ce out to the working tree. + * + * When topath[] is not NULL, instead of writing to the working tree + * file named by ce, a temporary file is created by this function and + * its name is returned in topath[], which must be able to hold at + * least TEMPORARY_FILENAME_LENGTH bytes long. + */ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { - static char path[PATH_MAX + 1]; + static struct strbuf path_buf = STRBUF_INIT; + char *path; struct stat st; - int len = state->base_dir_len; + int len; if (topath) return write_entry(ce, topath, state, 1); - memcpy(path, state->base_dir, len); - strcpy(path + len, ce->name); - len += ce_namelen(ce); + strbuf_reset(&path_buf); + strbuf_add(&path_buf, state->base_dir, state->base_dir_len); + strbuf_add(&path_buf, ce->name, ce_namelen(ce)); + path = path_buf.buf; + len = path_buf.len; if (!check_path(path, len, &st, state->base_dir_len)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); diff --git a/environment.c b/environment.c index 378254c77a..0a15349cfe 100644 --- a/environment.c +++ b/environment.c @@ -155,11 +155,6 @@ int is_bare_repository(void) return is_bare_repository_cfg && !get_git_work_tree(); } -int have_git_dir(void) -{ - return !!git_dir; -} - const char *get_git_dir(void) { if (!git_dir) diff --git a/fetch-pack.c b/fetch-pack.c index a0e0350ae6..1042448fa0 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -747,6 +747,10 @@ static int get_pack(struct fetch_pack_args *args, close(cmd.out); } + if (!use_sideband) + /* Closed by start_command() */ + xd[0] = -1; + ret = finish_command(&cmd); if (!ret || (args->check_self_contained_and_connected && ret == 1)) args->self_contained_and_connected = diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 51563840f4..24bb1ab992 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -263,6 +263,17 @@ sub get_empty_tree { return '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; } +sub get_diff_reference { + my $ref = shift; + if (defined $ref and $ref ne 'HEAD') { + return $ref; + } elsif (is_initial_commit()) { + return get_empty_tree(); + } else { + return 'HEAD'; + } +} + # Returns list of hashes, contents of each of which are: # VALUE: pathname # BINARY: is a binary path @@ -286,14 +297,7 @@ sub list_modified { return if (!@tracked); } - my $reference; - if (defined $patch_mode_revision and $patch_mode_revision ne 'HEAD') { - $reference = $patch_mode_revision; - } elsif (is_initial_commit()) { - $reference = get_empty_tree(); - } else { - $reference = 'HEAD'; - } + my $reference = get_diff_reference($patch_mode_revision); for (run_cmd_pipe(qw(git diff-index --cached --numstat --summary), $reference, '--', @tracked)) { @@ -737,7 +741,7 @@ sub parse_diff { splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}"; } if (defined $patch_mode_revision) { - push @diff_cmd, $patch_mode_revision; + push @diff_cmd, get_diff_reference($patch_mode_revision); } my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path); my @colored = (); diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 74d1cc7db0..95e69b19a7 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -430,10 +430,10 @@ sub req_validrequests $log->debug("req_validrequests"); - $log->debug("SEND : Valid-requests " . join(" ",keys %$methods)); + $log->debug("SEND : Valid-requests " . join(" ",sort keys %$methods)); $log->debug("SEND : ok"); - print "Valid-requests " . join(" ",keys %$methods) . "\n"; + print "Valid-requests " . join(" ",sort keys %$methods) . "\n"; print "ok\n"; } @@ -2124,7 +2124,7 @@ sub req_diff print "M retrieving revision $meta2->{revision}\n" } print "M diff "; - foreach my $opt ( keys %{$state->{opt}} ) + foreach my $opt ( sort keys %{$state->{opt}} ) { if ( ref $state->{opt}{$opt} eq "ARRAY" ) { @@ -4050,7 +4050,7 @@ sub update close FILELIST; # Detect deleted files - foreach my $file ( keys %$head ) + foreach my $file ( sort keys %$head ) { unless ( exists $seen_files->{$file} or $head->{$file}{filehash} eq "deleted" ) { @@ -4078,7 +4078,7 @@ sub update } $self->delete_head(); - foreach my $file ( keys %$head ) + foreach my $file ( sort keys %$head ) { $self->insert_head( $file, diff --git a/git-web--browse.sh b/git-web--browse.sh index 1d72ec760e..ebdfba6c94 100755 --- a/git-web--browse.sh +++ b/git-web--browse.sh @@ -34,7 +34,7 @@ valid_tool() { firefox | iceweasel | seamonkey | iceape | \ chrome | google-chrome | chromium | chromium-browser | \ konqueror | opera | w3m | elinks | links | lynx | dillo | open | \ - start | cygstart) + start | cygstart | xdg-open) ;; # happy *) valid_custom_tool "$1" || return 1 @@ -112,7 +112,7 @@ fi if test -z "$browser" ; then if test -n "$DISPLAY"; then - browser_candidates="firefox iceweasel google-chrome chrome chromium chromium-browser konqueror opera seamonkey iceape w3m elinks links lynx dillo" + browser_candidates="firefox iceweasel google-chrome chrome chromium chromium-browser konqueror opera seamonkey iceape w3m elinks links lynx dillo xdg-open" if test "$KDE_FULL_SESSION" = "true"; then browser_candidates="konqueror $browser_candidates" fi @@ -179,7 +179,7 @@ konqueror) ;; esac ;; -w3m|elinks|links|lynx|open|cygstart) +w3m|elinks|links|lynx|open|cygstart|xdg-open) "$browser_path" "$@" ;; start) @@ -801,10 +801,10 @@ static int graph_draw_octopus_merge(struct git_graph *graph, int num_dashes = ((graph->num_parents - dashless_commits) * 2) - 1; for (i = 0; i < num_dashes; i++) { - col_num = (i / 2) + dashless_commits; + col_num = (i / 2) + dashless_commits + graph->commit_index; strbuf_write_column(sb, &graph->new_columns[col_num], '-'); } - col_num = (i / 2) + dashless_commits; + col_num = (i / 2) + dashless_commits + graph->commit_index; strbuf_write_column(sb, &graph->new_columns[col_num], '.'); return num_dashes + 1; } diff --git a/http-push.c b/http-push.c index 69200baf76..34cb70f90e 100644 --- a/http-push.c +++ b/http-push.c @@ -1542,7 +1542,7 @@ static int remote_exists(const char *path) sprintf(url, "%s%s", repo->url, path); - switch (http_get_strbuf(url, NULL, NULL, 0)) { + switch (http_get_strbuf(url, NULL, NULL)) { case HTTP_OK: ret = 1; break; @@ -1566,7 +1566,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1) url = xmalloc(strlen(repo->url) + strlen(path) + 1); sprintf(url, "%s%s", repo->url, path); - if (http_get_strbuf(url, NULL, &buffer, 0) != HTTP_OK) + if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK) die("Couldn't get %s for remote symref\n%s", url, curl_errorstr); free(url); @@ -47,7 +47,7 @@ static int curl_ftp_no_epsv; static const char *curl_http_proxy; static const char *curl_cookie_file; static int curl_save_cookies; -static struct credential http_auth = CREDENTIAL_INIT; +struct credential http_auth = CREDENTIAL_INIT; static int http_proactive_auth; static const char *user_agent; @@ -260,6 +260,42 @@ static int has_cert_password(void) return 1; } +#if LIBCURL_VERSION_NUM >= 0x071900 +static void set_curl_keepalive(CURL *c) +{ + curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1); +} + +#elif LIBCURL_VERSION_NUM >= 0x071000 +static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type) +{ + int ka = 1; + int rc; + socklen_t len = (socklen_t)sizeof(ka); + + if (type != CURLSOCKTYPE_IPCXN) + return 0; + + rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len); + if (rc < 0) + warning("unable to set SO_KEEPALIVE on socket %s", + strerror(errno)); + + return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */ +} + +static void set_curl_keepalive(CURL *c) +{ + curl_easy_setopt(c, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); +} + +#else +static void set_curl_keepalive(CURL *c) +{ + /* not supported on older curl versions */ +} +#endif + static CURL *get_curl_handle(void) { CURL *result = curl_easy_init(); @@ -332,6 +368,8 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY); } + set_curl_keepalive(result); + return result; } @@ -823,7 +861,6 @@ int handle_curl_result(struct slot_results *results) credential_reject(&http_auth); return HTTP_NOAUTH; } else { - credential_fill(&http_auth); return HTTP_REAUTH; } } else { @@ -837,12 +874,25 @@ int handle_curl_result(struct slot_results *results) } } +static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf) +{ + char *ptr; + CURLcode ret; + + strbuf_reset(buf); + ret = curl_easy_getinfo(curl, info, &ptr); + if (!ret && ptr) + strbuf_addstr(buf, ptr); + return ret; +} + /* http_request() targets */ #define HTTP_REQUEST_STRBUF 0 #define HTTP_REQUEST_FILE 1 -static int http_request(const char *url, struct strbuf *type, - void *result, int target, int options) +static int http_request(const char *url, + void *result, int target, + const struct http_get_options *options) { struct active_request_slot *slot; struct slot_results results; @@ -875,9 +925,9 @@ static int http_request(const char *url, struct strbuf *type, } strbuf_addstr(&buf, "Pragma:"); - if (options & HTTP_NO_CACHE) + if (options && options->no_cache) strbuf_addstr(&buf, " no-cache"); - if (options & HTTP_KEEP_ERROR) + if (options && options->keep_error) curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0); headers = curl_slist_append(headers, buf.buf); @@ -895,13 +945,13 @@ static int http_request(const char *url, struct strbuf *type, ret = HTTP_START_FAILED; } - if (type) { - char *t; - strbuf_reset(type); - curl_easy_getinfo(slot->curl, CURLINFO_CONTENT_TYPE, &t); - if (t) - strbuf_addstr(type, t); - } + if (options && options->content_type) + curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, + options->content_type); + + if (options && options->effective_url) + curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL, + options->effective_url); curl_slist_free_all(headers); strbuf_release(&buf); @@ -909,12 +959,71 @@ static int http_request(const char *url, struct strbuf *type, return ret; } +/* + * Update the "base" url to a more appropriate value, as deduced by + * redirects seen when requesting a URL starting with "url". + * + * The "asked" parameter is a URL that we asked curl to access, and must begin + * with "base". + * + * The "got" parameter is the URL that curl reported to us as where we ended + * up. + * + * Returns 1 if we updated the base url, 0 otherwise. + * + * Our basic strategy is to compare "base" and "asked" to find the bits + * specific to our request. We then strip those bits off of "got" to yield the + * new base. So for example, if our base is "http://example.com/foo.git", + * and we ask for "http://example.com/foo.git/info/refs", we might end up + * with "https://other.example.com/foo.git/info/refs". We would want the + * new URL to become "https://other.example.com/foo.git". + * + * Note that this assumes a sane redirect scheme. It's entirely possible + * in the example above to end up at a URL that does not even end in + * "info/refs". In such a case we simply punt, as there is not much we can + * do (and such a scheme is unlikely to represent a real git repository, + * which means we are likely about to abort anyway). + */ +static int update_url_from_redirect(struct strbuf *base, + const char *asked, + const struct strbuf *got) +{ + const char *tail; + size_t tail_len; + + if (!strcmp(asked, got->buf)) + return 0; + + if (prefixcmp(asked, base->buf)) + die("BUG: update_url_from_redirect: %s is not a superset of %s", + asked, base->buf); + + tail = asked + base->len; + tail_len = strlen(tail); + + if (got->len < tail_len || + strcmp(tail, got->buf + got->len - tail_len)) + return 0; /* insane redirect scheme */ + + strbuf_reset(base); + strbuf_add(base, got->buf, got->len - tail_len); + return 1; +} + static int http_request_reauth(const char *url, - struct strbuf *type, void *result, int target, - int options) + struct http_get_options *options) { - int ret = http_request(url, type, result, target, options); + int ret = http_request(url, result, target, options); + + if (options && options->effective_url && options->base_url) { + if (update_url_from_redirect(options->base_url, + url, options->effective_url)) { + credential_from_url(&http_auth, options->base_url->buf); + url = options->effective_url->buf; + } + } + if (ret != HTTP_REAUTH) return ret; @@ -924,7 +1033,7 @@ static int http_request_reauth(const char *url, * making our next request. We only know how to do this for * the strbuf case, but that is enough to satisfy current callers. */ - if (options & HTTP_KEEP_ERROR) { + if (options && options->keep_error) { switch (target) { case HTTP_REQUEST_STRBUF: strbuf_reset(result); @@ -933,15 +1042,17 @@ static int http_request_reauth(const char *url, die("BUG: HTTP_KEEP_ERROR is only supported with strbufs"); } } - return http_request(url, type, result, target, options); + + credential_fill(&http_auth); + + return http_request(url, result, target, options); } int http_get_strbuf(const char *url, - struct strbuf *type, - struct strbuf *result, int options) + struct strbuf *result, + struct http_get_options *options) { - return http_request_reauth(url, type, result, - HTTP_REQUEST_STRBUF, options); + return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options); } /* @@ -950,7 +1061,8 @@ int http_get_strbuf(const char *url, * If a previous interrupted download is detected (i.e. a previous temporary * file is still around) the download is resumed. */ -static int http_get_file(const char *url, const char *filename, int options) +static int http_get_file(const char *url, const char *filename, + struct http_get_options *options) { int ret; struct strbuf tmpfile = STRBUF_INIT; @@ -958,16 +1070,16 @@ static int http_get_file(const char *url, const char *filename, int options) strbuf_addf(&tmpfile, "%s.temp", filename); result = fopen(tmpfile.buf, "a"); - if (! result) { + if (!result) { error("Unable to open local file %s", tmpfile.buf); ret = HTTP_ERROR; goto cleanup; } - ret = http_request_reauth(url, NULL, result, HTTP_REQUEST_FILE, options); + ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options); fclose(result); - if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename)) + if (ret == HTTP_OK && move_temp_to_file(tmpfile.buf, filename)) ret = HTTP_ERROR; cleanup: strbuf_release(&tmpfile); @@ -976,12 +1088,15 @@ cleanup: int http_fetch_ref(const char *base, struct ref *ref) { + struct http_get_options options = {0}; char *url; struct strbuf buffer = STRBUF_INIT; int ret = -1; + options.no_cache = 1; + url = quote_ref_url(base, ref->name); - if (http_get_strbuf(url, NULL, &buffer, HTTP_NO_CACHE) == HTTP_OK) { + if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) { strbuf_rtrim(&buffer); if (buffer.len == 40) ret = get_sha1_hex(buffer.buf, ref->old_sha1); @@ -1012,7 +1127,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url) strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1)); tmp = strbuf_detach(&buf, NULL); - if (http_get_file(url, tmp, 0) != HTTP_OK) { + if (http_get_file(url, tmp, NULL) != HTTP_OK) { error("Unable to get pack index %s", url); free(tmp); tmp = NULL; @@ -1065,6 +1180,7 @@ add_pack: int http_get_info_packs(const char *base_url, struct packed_git **packs_head) { + struct http_get_options options = {0}; int ret = 0, i = 0; char *url, *data; struct strbuf buf = STRBUF_INIT; @@ -1074,7 +1190,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head) strbuf_addstr(&buf, "objects/info/packs"); url = strbuf_detach(&buf, NULL); - ret = http_get_strbuf(url, NULL, &buf, HTTP_NO_CACHE); + options.no_cache = 1; + ret = http_get_strbuf(url, &buf, &options); if (ret != HTTP_OK) goto cleanup; @@ -102,6 +102,7 @@ extern void http_cleanup(void); extern int active_requests; extern int http_is_verbose; extern size_t http_post_buffer; +extern struct credential http_auth; extern char curl_errorstr[CURL_ERROR_SIZE]; @@ -125,11 +126,30 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url, extern char *get_remote_object_url(const char *url, const char *hex, int only_two_digit_prefix); -/* Options for http_request_*() */ -#define HTTP_NO_CACHE 1 -#define HTTP_KEEP_ERROR 2 +/* Options for http_get_*() */ +struct http_get_options { + unsigned no_cache:1, + keep_error:1; + + /* If non-NULL, returns the content-type of the response. */ + struct strbuf *content_type; + + /* + * If non-NULL, returns the URL we ended up at, including any + * redirects we followed. + */ + struct strbuf *effective_url; + + /* + * If both base_url and effective_url are non-NULL, the base URL will + * be munged to reflect any redirections going from the requested url + * to effective_url. See the definition of update_url_from_redirect + * for details. + */ + struct strbuf *base_url; +}; -/* Return values for http_request_*() */ +/* Return values for http_get_*() */ #define HTTP_OK 0 #define HTTP_MISSING_TARGET 1 #define HTTP_ERROR 2 @@ -142,7 +162,7 @@ extern char *get_remote_object_url(const char *url, const char *hex, * * If the result pointer is NULL, a HTTP HEAD request is made instead of GET. */ -int http_get_strbuf(const char *url, struct strbuf *content_type, struct strbuf *result, int options); +int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options); extern int http_fetch_ref(const char *base, struct ref *ref); @@ -233,7 +233,21 @@ int split_ident_line(struct ident_split *split, const char *line, int len) if (!split->mail_end) return status; - for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++) + /* + * Look from the end-of-line to find the trailing ">" of the mail + * address, even though we should already know it as split->mail_end. + * This can help in cases of broken idents with an extra ">" somewhere + * in the email address. Note that we are assuming the timestamp will + * never have a ">" in it. + * + * Note that we will always find some ">" before going off the front of + * the string, because will always hit the split->mail_end closing + * bracket. + */ + for (cp = line + len - 1; *cp != '>'; cp--) + ; + + for (cp = cp + 1; cp < line + len && isspace(*cp); cp++) ; if (line + len <= cp) goto person_only; diff --git a/line-log.c b/line-log.c index 8b6e497b3f..717638b333 100644 --- a/line-log.c +++ b/line-log.c @@ -760,7 +760,8 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list r = r->next; } paths[count] = NULL; - parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths); + parse_pathspec(&rev->diffopt.pathspec, 0, + PATHSPEC_PREFER_FULL, "", paths); free(paths); } } @@ -434,6 +434,16 @@ int adjust_shared_perm(const char *path) return 0; } +static int have_same_root(const char *path1, const char *path2) +{ + int is_abs1, is_abs2; + + is_abs1 = is_absolute_path(path1); + is_abs2 = is_absolute_path(path2); + return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) || + (!is_abs1 && !is_abs2); +} + /* * Give path as relative to prefix. * @@ -454,6 +464,16 @@ const char *relative_path(const char *in, const char *prefix, else if (!prefix_len) return in; + if (have_same_root(in, prefix)) { + /* bypass dos_drive, for "c:" is identical to "C:" */ + if (has_dos_drive_prefix(in)) { + i = 2; + j = 2; + } + } else { + return in; + } + while (i < prefix_len && j < in_len && prefix[i] == in[j]) { if (is_dir_sep(prefix[i])) { while (is_dir_sep(prefix[i])) @@ -531,6 +551,51 @@ const char *relative_path(const char *in, const char *prefix, } /* + * A simpler implementation of relative_path + * + * Get relative path by removing "prefix" from "in". This function + * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter + * to increase performance when traversing the path to work_tree. + */ +const char *remove_leading_path(const char *in, const char *prefix) +{ + static char buf[PATH_MAX + 1]; + int i = 0, j = 0; + + if (!prefix || !prefix[0]) + return in; + while (prefix[i]) { + if (is_dir_sep(prefix[i])) { + if (!is_dir_sep(in[j])) + return in; + while (is_dir_sep(prefix[i])) + i++; + while (is_dir_sep(in[j])) + j++; + continue; + } else if (in[j] != prefix[i]) { + return in; + } + i++; + j++; + } + if ( + /* "/foo" is a prefix of "/foo" */ + in[j] && + /* "/foo" is not a prefix of "/foobar" */ + !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j]) + ) + return in; + while (is_dir_sep(in[j])) + j++; + if (!in[j]) + strcpy(buf, "."); + else + strcpy(buf, in + j); + return buf; +} + +/* * It is okay if dst == src, but they should not overlap otherwise. * * Performs the following normalizations on src, storing the result in dst: @@ -947,13 +947,6 @@ static struct ref_cache *get_ref_cache(const char *submodule) return refs; } -void invalidate_ref_cache(const char *submodule) -{ - struct ref_cache *refs = get_ref_cache(submodule); - clear_packed_ref_cache(refs); - clear_loose_ref_cache(refs); -} - /* The length of a peeled reference line in packed-refs, including EOL: */ #define PEELED_LINE_LENGTH 42 @@ -3376,7 +3369,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict) size_t total_len = 0; /* the rule list is NULL terminated, count them first */ - for (; ref_rev_parse_rules[nr_rules]; nr_rules++) + for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++) /* no +1 because strlen("%s") < strlen("%.*s") */ total_len += strlen(ref_rev_parse_rules[nr_rules]); @@ -165,14 +165,6 @@ extern void unlock_ref(struct ref_lock *lock); /** Writes sha1 into the ref specified by the lock. **/ extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); -/* - * Invalidate the reference cache for the specified submodule. Use - * submodule=NULL to invalidate the cache for the main module. This - * function must be called if references are changed via a mechanism - * other than the refs API. - */ -extern void invalidate_ref_cache(const char *submodule); - /** Setup reflog before using. **/ int log_ref_setup(const char *ref_name, char *logfile, int bufsize); diff --git a/remote-curl.c b/remote-curl.c index b5ebe01800..c9b891adbf 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -9,9 +9,11 @@ #include "string-list.h" #include "sideband.h" #include "argv-array.h" +#include "credential.h" static struct remote *remote; -static const char *url; /* always ends with a trailing slash */ +/* always ends with a trailing slash */ +static struct strbuf url = STRBUF_INIT; struct options { int verbosity; @@ -130,7 +132,8 @@ static struct ref *parse_info_refs(struct discovery *heads) mid = &data[i]; if (data[i] == '\n') { if (mid - start != 40) - die("%sinfo/refs not valid: is this a git repository?", url); + die("%sinfo/refs not valid: is this a git repository?", + url.buf); data[i] = 0; ref_name = mid + 1; ref = xmalloc(sizeof(struct ref) + @@ -149,7 +152,7 @@ static struct ref *parse_info_refs(struct discovery *heads) } ref = alloc_ref("HEAD"); - if (!http_fetch_ref(url, ref) && + if (!http_fetch_ref(url.buf, ref) && !resolve_remote_symref(ref, refs)) { ref->next = refs; refs = ref; @@ -203,40 +206,47 @@ static struct discovery* discover_refs(const char *service, int for_push) struct strbuf exp = STRBUF_INIT; struct strbuf type = STRBUF_INIT; struct strbuf buffer = STRBUF_INIT; + struct strbuf refs_url = STRBUF_INIT; + struct strbuf effective_url = STRBUF_INIT; struct discovery *last = last_discovery; - char *refs_url; int http_ret, maybe_smart = 0; + struct http_get_options options; if (last && !strcmp(service, last->service)) return last; free_discovery(last); - strbuf_addf(&buffer, "%sinfo/refs", url); - if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) && + strbuf_addf(&refs_url, "%sinfo/refs", url.buf); + if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) && git_env_bool("GIT_SMART_HTTP", 1)) { maybe_smart = 1; - if (!strchr(url, '?')) - strbuf_addch(&buffer, '?'); + if (!strchr(url.buf, '?')) + strbuf_addch(&refs_url, '?'); else - strbuf_addch(&buffer, '&'); - strbuf_addf(&buffer, "service=%s", service); + strbuf_addch(&refs_url, '&'); + strbuf_addf(&refs_url, "service=%s", service); } - refs_url = strbuf_detach(&buffer, NULL); - http_ret = http_get_strbuf(refs_url, &type, &buffer, - HTTP_NO_CACHE | HTTP_KEEP_ERROR); + memset(&options, 0, sizeof(options)); + options.content_type = &type; + options.effective_url = &effective_url; + options.base_url = &url; + options.no_cache = 1; + options.keep_error = 1; + + http_ret = http_get_strbuf(refs_url.buf, &buffer, &options); switch (http_ret) { case HTTP_OK: break; case HTTP_MISSING_TARGET: show_http_message(&type, &buffer); - die("repository '%s' not found", url); + die("repository '%s' not found", url.buf); case HTTP_NOAUTH: show_http_message(&type, &buffer); - die("Authentication failed for '%s'", url); + die("Authentication failed for '%s'", url.buf); default: show_http_message(&type, &buffer); - die("unable to access '%s': %s", url, curl_errorstr); + die("unable to access '%s': %s", url.buf, curl_errorstr); } last= xcalloc(1, sizeof(*last_discovery)); @@ -277,9 +287,10 @@ static struct discovery* discover_refs(const char *service, int for_push) else last->refs = parse_info_refs(last); - free(refs_url); + strbuf_release(&refs_url); strbuf_release(&exp); strbuf_release(&type); + strbuf_release(&effective_url); strbuf_release(&buffer); last_discovery = last; return last; @@ -463,6 +474,8 @@ static int post_rpc(struct rpc_state *rpc) if (large_request) { do { err = probe_rpc(rpc); + if (err == HTTP_REAUTH) + credential_fill(&http_auth); } while (err == HTTP_REAUTH); if (err != HTTP_OK) return -1; @@ -562,8 +575,10 @@ retry: curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); err = run_slot(slot); - if (err == HTTP_REAUTH && !large_request) + if (err == HTTP_REAUTH && !large_request) { + credential_fill(&http_auth); goto retry; + } if (err != HTTP_OK) err = -1; @@ -598,7 +613,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads) rpc->out = client.out; strbuf_init(&rpc->result, 0); - strbuf_addf(&buf, "%s%s", url, svc); + strbuf_addf(&buf, "%s%s", url.buf, svc); rpc->service_url = strbuf_detach(&buf, NULL); strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc); @@ -650,7 +665,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch) for (i = 0; i < nr_heads; i++) targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1)); - walker = get_http_walker(url); + walker = get_http_walker(url.buf); walker->get_all = 1; walker->get_tree = 1; walker->get_history = 1; @@ -697,7 +712,7 @@ static int fetch_git(struct discovery *heads, depth_arg = strbuf_detach(&buf, NULL); argv[argc++] = depth_arg; } - argv[argc++] = url; + argv[argc++] = url.buf; argv[argc++] = NULL; for (i = 0; i < nr_heads; i++) { @@ -795,7 +810,7 @@ static int push_dav(int nr_spec, char **specs) argv[argc++] = "--dry-run"; if (options.verbosity > 1) argv[argc++] = "--verbose"; - argv[argc++] = url; + argv[argc++] = url.buf; for (i = 0; i < nr_spec; i++) argv[argc++] = specs[i]; argv[argc++] = NULL; @@ -828,7 +843,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs) argv_array_push(&args, options.progress ? "--progress" : "--no-progress"); for_each_string_list_item(cas_option, &cas_options) argv_array_push(&args, cas_option->string); - argv_array_push(&args, url); + argv_array_push(&args, url.buf); for (i = 0; i < nr_spec; i++) argv_array_push(&args, specs[i]); @@ -909,14 +924,12 @@ int main(int argc, const char **argv) remote = remote_get(argv[1]); if (argc > 2) { - end_url_with_slash(&buf, argv[2]); + end_url_with_slash(&url, argv[2]); } else { - end_url_with_slash(&buf, remote->url[0]); + end_url_with_slash(&url, remote->url[0]); } - url = strbuf_detach(&buf, NULL); - - http_init(remote, url, 0); + http_init(remote, url.buf, 0); do { if (strbuf_getline(&buf, stdin, '\n') == EOF) { @@ -483,7 +483,7 @@ static void read_config(void) int flag; if (default_remote_name) /* did this already */ return; - default_remote_name = xstrdup("origin"); + default_remote_name = "origin"; current_branch = NULL; head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag); if (head_ref && (flag & REF_ISSYMREF) && diff --git a/revision.c b/revision.c index 0173e0148b..3fdea51ffe 100644 --- a/revision.c +++ b/revision.c @@ -1372,7 +1372,8 @@ static void prepare_show_merge(struct rev_info *revs) i++; } free_pathspec(&revs->prune_data); - parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune); + parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, + PATHSPEC_PREFER_FULL, "", prune); revs->limited = 1; } @@ -1419,26 +1420,40 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi } if (!get_sha1_committish(this, from_sha1) && !get_sha1_committish(next, sha1)) { - struct commit *a, *b; - struct commit_list *exclude; - - a = lookup_commit_reference(from_sha1); - b = lookup_commit_reference(sha1); - if (!a || !b) { - if (revs->ignore_missing) - return 0; - die(symmetric ? - "Invalid symmetric difference expression %s...%s" : - "Invalid revision range %s..%s", - arg, next); - } + struct object *a_obj, *b_obj; if (!cant_be_filename) { *dotdot = '.'; verify_non_filename(revs->prefix, arg); } - if (symmetric) { + a_obj = parse_object(from_sha1); + b_obj = parse_object(sha1); + if (!a_obj || !b_obj) { + missing: + if (revs->ignore_missing) + return 0; + die(symmetric + ? "Invalid symmetric difference expression %s" + : "Invalid revision range %s", arg); + } + + if (!symmetric) { + /* just A..B */ + a_flags = flags_exclude; + } else { + /* A...B -- find merge bases between the two */ + struct commit *a, *b; + struct commit_list *exclude; + + a = (a_obj->type == OBJ_COMMIT + ? (struct commit *)a_obj + : lookup_commit_reference(a_obj->sha1)); + b = (b_obj->type == OBJ_COMMIT + ? (struct commit *)b_obj + : lookup_commit_reference(b_obj->sha1)); + if (!a || !b) + goto missing; exclude = get_merge_bases(a, b, 1); add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, @@ -1446,17 +1461,18 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi add_pending_commit_list(revs, exclude, flags_exclude); free_commit_list(exclude); + a_flags = flags | SYMMETRIC_LEFT; - } else - a_flags = flags_exclude; - a->object.flags |= a_flags; - b->object.flags |= flags; - add_rev_cmdline(revs, &a->object, this, + } + + a_obj->flags |= a_flags; + b_obj->flags |= flags; + add_rev_cmdline(revs, a_obj, this, REV_CMD_LEFT, a_flags); - add_rev_cmdline(revs, &b->object, next, + add_rev_cmdline(revs, b_obj, next, REV_CMD_RIGHT, flags); - add_pending_object(revs, &a->object, this); - add_pending_object(revs, &b->object, next); + add_pending_object(revs, a_obj, this); + add_pending_object(revs, b_obj, next); return 0; } *dotdot = '.'; diff --git a/send-pack.c b/send-pack.c index b228d65613..fab62e3da0 100644 --- a/send-pack.c +++ b/send-pack.c @@ -302,8 +302,12 @@ int send_pack(struct send_pack_args *args, shutdown(fd[0], SHUT_WR); if (use_sideband) finish_async(&demux); + fd[1] = -1; return -1; } + if (!args->stateless_rpc) + /* Closed by pack_objects() via start_command() */ + fd[1] = -1; } if (args->stateless_rpc && cmds_sent) packet_flush(out); @@ -227,7 +227,6 @@ int is_inside_work_tree(void) void setup_work_tree(void) { - struct strbuf sb = STRBUF_INIT; const char *work_tree, *git_dir; static int initialized = 0; @@ -247,10 +246,8 @@ void setup_work_tree(void) if (getenv(GIT_WORK_TREE_ENVIRONMENT)) setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1); - set_git_dir(relative_path(git_dir, work_tree, &sb)); + set_git_dir(remove_leading_path(git_dir, work_tree)); initialized = 1; - - strbuf_release(&sb); } static int check_repository_format_gently(const char *gitdir, int *nongit_ok) diff --git a/sha1_file.c b/sha1_file.c index f80bbe4674..7dadd04cb7 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -2514,7 +2514,6 @@ static int sha1_loose_object_info(const unsigned char *sha1, return 0; } -/* returns enum object_type or negative */ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) { struct cached_object *co; @@ -2563,6 +2562,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) return 0; } +/* returns enum object_type or negative */ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) { enum object_type type; @@ -340,7 +340,11 @@ Don't: - use perl without spelling it as "$PERL_PATH". This is to help our friends on Windows where the platform Perl often adds CR before the end of line, and they bundle Git with a version of Perl that - does not do so, whose path is specified with $PERL_PATH. + does not do so, whose path is specified with $PERL_PATH. Note that we + provide a "perl" function which uses $PERL_PATH under the hood, so + you do not need to worry when simply running perl in the test scripts + (but you do, for example, on a shebang line or in a sub script + created via "write_script"). - use sh without spelling it as "$SHELL_PATH", when the script can be misinterpreted by broken platform shell (e.g. Solaris). @@ -387,7 +391,7 @@ of the test_* functions (see the "Test harness library" section below), e.g.: test_expect_success PERL 'I need Perl' ' - "$PERL_PATH" -e "hlagh() if unf_unf()" + perl -e "hlagh() if unf_unf()" ' The advantage of skipping tests like this is that platforms that don't @@ -520,7 +524,7 @@ library for your script to use. test_external \ 'GitwebCache::*FileCache*' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl + perl "$TEST_DIRECTORY"/t9503/test_cache_interface.pl If the test is outputting its own TAP you should set the test_external_has_tap variable somewhere before calling the first @@ -536,7 +540,7 @@ library for your script to use. test_external_without_stderr \ 'Perl API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl + perl "$TEST_DIRECTORY"/t9700/test.pl - test_expect_code <exit-code> <command> @@ -629,11 +633,18 @@ See the prereq argument to the test_* functions in the "Test harness library" section above and the "test_have_prereq" function for how to use these, and "test_set_prereq" for how to define your own. - - PERL & PYTHON + - PYTHON - Git wasn't compiled with NO_PERL=YesPlease or - NO_PYTHON=YesPlease. Wrap any tests that need Perl or Python in - these. + Git wasn't compiled with NO_PYTHON=YesPlease. Wrap any tests that + need Python with this. + + - PERL + + Git wasn't compiled with NO_PERL=YesPlease. + + Even without the PERL prerequisite, tests can assume there is a + usable perl interpreter at $PERL_PATH, though it need not be + particularly modern. - POSIXPERM diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index 9e381e000f..8cf909a6c5 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -69,7 +69,7 @@ gitweb_run () { # written to web server logs, so we are not interested in that: # we are interested only in properly formatted errors/warnings rm -f gitweb.log && - "$PERL_PATH" -- "$SCRIPT_NAME" \ + perl -- "$SCRIPT_NAME" \ >gitweb.output 2>gitweb.log && perl -w -e ' open O, ">gitweb.headers"; diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 3c43ff11b3..957ae936e8 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -18,10 +18,6 @@ check() { cat stderr && false fi && - if test_have_prereq MINGW - then - dos2unix -q stderr - fi && test_cmp expect-stdout stdout && test_cmp expect-stderr stderr } diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index c5e55b190b..b0ec12ff6c 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -29,7 +29,7 @@ export svnrepo svnconf=$PWD/svnconf export svnconf -"$PERL_PATH" -w -e " +perl -w -e " use SVN::Core; use SVN::Repos; \$SVN::Core::VERSION gt '1.1.0' or exit(42); @@ -146,7 +146,7 @@ stop_httpd () { } convert_to_rev_db () { - "$PERL_PATH" -w -- - "$@" <<\EOF + perl -w -- - "$@" <<\EOF use strict; @ARGV == 2 or die "usage: convert_to_rev_db <input> <output>"; open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]"; diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 54dbbfe5ce..ad8f1ef71e 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -204,7 +204,8 @@ set_askpass() { } expect_askpass() { - dest=$HTTPD_DEST + dest=$HTTPD_DEST${3+/$3} + { case "$1" in none) diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 397c480401..3a03e8263d 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -110,6 +110,8 @@ ScriptAlias /broken_smart/ broken-smart-http.sh/ RewriteEngine on RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301] RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302] +RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301] +RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301] <IfDefine SSL> LoadModule ssl_module modules/mod_ssl.so diff --git a/t/lib-pack.sh b/t/lib-pack.sh index 7e8685b44c..b96e1254dd 100644 --- a/t/lib-pack.sh +++ b/t/lib-pack.sh @@ -12,10 +12,10 @@ # Print the big-endian 4-byte octal representation of $1 uint32_octal () { n=$1 - printf '\%o' $(($n / 16777216)); n=$((n % 16777216)) - printf '\%o' $(($n / 65536)); n=$((n % 65536)) - printf '\%o' $(($n / 256)); n=$((n % 256)) - printf '\%o' $(($n )); + printf '\\%o' $(($n / 16777216)); n=$((n % 16777216)) + printf '\\%o' $(($n / 65536)); n=$((n % 65536)) + printf '\\%o' $(($n / 256)); n=$((n % 256)) + printf '\\%o' $(($n )); } # Print the big-endian 4-byte binary representation of $1 diff --git a/t/lib-terminal.sh b/t/lib-terminal.sh index 58d911d21b..737df289a1 100644 --- a/t/lib-terminal.sh +++ b/t/lib-terminal.sh @@ -19,7 +19,7 @@ test_expect_success PERL 'set up terminal for tests' ' then : elif - "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl \ + perl "$TEST_DIRECTORY"/test-terminal.perl \ sh -c "test -t 1 && test -t 2" then test_set_prereq TTY && @@ -29,7 +29,7 @@ test_expect_success PERL 'set up terminal for tests' ' echo >&4 "test_terminal: need to declare TTY prerequisite" return 127 fi - "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl "$@" + perl "$TEST_DIRECTORY"/test-terminal.perl "$@" } fi ' diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 2bd5e32745..07c10c8dca 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -190,33 +190,37 @@ test_expect_success SYMLINKS 'real path works on symlinks' ' test "$sym" = "$(test-path-utils real_path "$dir2/syml")" ' -relative_path /a/b/c/ /a/b/ c/ -relative_path /a/b/c/ /a/b c/ -relative_path /a//b//c/ //a/b// c/ POSIX -relative_path /a/b /a/b ./ -relative_path /a/b/ /a/b ./ -relative_path /a /a/b ../ -relative_path / /a/b/ ../../ -relative_path /a/c /a/b/ ../c -relative_path /a/c /a/b ../c -relative_path /x/y /a/b/ ../../x/y -relative_path /a/b "<empty>" /a/b -relative_path /a/b "<null>" /a/b -relative_path a/b/c/ a/b/ c/ -relative_path a/b/c/ a/b c/ -relative_path a/b//c a//b c -relative_path a/b/ a/b/ ./ -relative_path a/b/ a/b ./ -relative_path a a/b ../ -relative_path x/y a/b ../../x/y -relative_path a/c a/b ../c -relative_path a/b "<empty>" a/b -relative_path a/b "<null>" a/b -relative_path "<empty>" /a/b ./ -relative_path "<empty>" "<empty>" ./ -relative_path "<empty>" "<null>" ./ -relative_path "<null>" "<empty>" ./ -relative_path "<null>" "<null>" ./ -relative_path "<null>" /a/b ./ +relative_path /foo/a/b/c/ /foo/a/b/ c/ +relative_path /foo/a/b/c/ /foo/a/b c/ +relative_path /foo/a//b//c/ ///foo/a/b// c/ POSIX +relative_path /foo/a/b /foo/a/b ./ +relative_path /foo/a/b/ /foo/a/b ./ +relative_path /foo/a /foo/a/b ../ +relative_path / /foo/a/b/ ../../../ +relative_path /foo/a/c /foo/a/b/ ../c +relative_path /foo/a/c /foo/a/b ../c +relative_path /foo/x/y /foo/a/b/ ../../x/y +relative_path /foo/a/b "<empty>" /foo/a/b +relative_path /foo/a/b "<null>" /foo/a/b +relative_path foo/a/b/c/ foo/a/b/ c/ +relative_path foo/a/b/c/ foo/a/b c/ +relative_path foo/a/b//c foo/a//b c +relative_path foo/a/b/ foo/a/b/ ./ +relative_path foo/a/b/ foo/a/b ./ +relative_path foo/a foo/a/b ../ +relative_path foo/x/y foo/a/b ../../x/y +relative_path foo/a/c foo/a/b ../c +relative_path foo/a/b /foo/x/y foo/a/b +relative_path /foo/a/b foo/x/y /foo/a/b +relative_path d:/a/b D:/a/c ../b MINGW +relative_path C:/a/b D:/a/c C:/a/b MINGW +relative_path foo/a/b "<empty>" foo/a/b +relative_path foo/a/b "<null>" foo/a/b +relative_path "<empty>" /foo/a/b ./ +relative_path "<empty>" "<empty>" ./ +relative_path "<empty>" "<null>" ./ +relative_path "<null>" "<empty>" ./ +relative_path "<null>" "<null>" ./ +relative_path "<null>" /foo/a/b ./ test_done diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh index 428ebb0080..a29d166e00 100755 --- a/t/t0202-gettext-perl.sh +++ b/t/t0202-gettext-perl.sh @@ -12,7 +12,7 @@ if ! test_have_prereq PERL; then test_done fi -"$PERL_PATH" -MTest::More -e 0 2>/dev/null || { +perl -MTest::More -e 0 2>/dev/null || { skip_all="Perl Test::More unavailable, skipping test" test_done } @@ -22,6 +22,6 @@ test_external_has_tap=1 test_external_without_stderr \ 'Perl Git::I18N API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t0202/test.pl + perl "$TEST_DIRECTORY"/t0202/test.pl test_done diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh index df573c4978..b946f87686 100755 --- a/t/t1010-mktree.sh +++ b/t/t1010-mktree.sh @@ -42,13 +42,13 @@ test_expect_success 'ls-tree piped to mktree (2)' ' ' test_expect_success 'ls-tree output in wrong order given to mktree (1)' ' - "$PERL_PATH" -e "print reverse <>" <top | + perl -e "print reverse <>" <top | git mktree >actual && test_cmp tree actual ' test_expect_success 'ls-tree output in wrong order given to mktree (2)' ' - "$PERL_PATH" -e "print reverse <>" <top.withsub | + perl -e "print reverse <>" <top.withsub | git mktree >actual && test_cmp tree.withsub actual ' diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh index 7cc0a3582e..87bdf9c96b 100755 --- a/t/t2010-checkout-ambiguous.sh +++ b/t/t2010-checkout-ambiguous.sh @@ -47,4 +47,10 @@ test_expect_success 'disambiguate checking out from a tree-ish' ' git diff --exit-code --quiet ' +test_expect_success 'accurate error message with more than one ref' ' + test_must_fail git checkout HEAD master -- 2>actual && + grep 2 actual && + test_i18ngrep "one reference expected, 2 given" actual +' + test_done diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh index 094b92ef48..6ecb559465 100755 --- a/t/t2024-checkout-dwim.sh +++ b/t/t2024-checkout-dwim.sh @@ -164,4 +164,25 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' ' test_branch_upstream eggs repo_d eggs ' +test_expect_success 'checkout of branch with a file having the same name fails' ' + git checkout -B master && + test_might_fail git branch -D spam && + + >spam && + test_must_fail git checkout spam && + test_must_fail git rev-parse --verify refs/heads/spam && + test_branch master +' + +test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' ' + git checkout -B master && + test_might_fail git branch -D spam && + + >spam && + git checkout spam -- && + test_branch spam && + test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD && + test_branch_upstream spam repo_c spam +' + test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 0fe7647928..fcdb867748 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -18,7 +18,7 @@ test_expect_success 'prepare a trivial repository' ' ' test_expect_success 'git branch --help should not have created a bogus branch' ' - test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null && + test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 && test_path_is_missing .git/refs/heads/--help ' diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index 7480d6e7c2..9a146f1335 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -69,7 +69,7 @@ test_expect_success 'ls-files -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git ls-files -z >ls-files.z && - "$PERL_PATH" -pe "y/\000/\012/" <ls-files.z >current && + perl -pe "y/\000/\012/" <ls-files.z >current && test_cmp expected current ' @@ -106,7 +106,7 @@ test_expect_success 'diff-index -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git diff-index -z --name-status $t0 >diff-index.z && - "$PERL_PATH" -pe "y/\000/\012/" <diff-index.z >current && + perl -pe "y/\000/\012/" <diff-index.z >current && test_cmp expected current ' @@ -116,7 +116,7 @@ test_expect_success 'diff-tree -z does not quote funny filename' ' tabs ," (dq) and spaces EOF git diff-tree -z --name-status $t0 $t1 >diff-tree.z && - "$PERL_PATH" -pe y/\\000/\\012/ <diff-tree.z >current && + perl -pe y/\\000/\\012/ <diff-tree.z >current && test_cmp expected current ' diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 639cb70941..540c49bab6 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -240,18 +240,15 @@ test_expect_success 'refresh index before checking if it is up-to-date' ' test_expect_success 'choking "git rm" should not let it die with cruft' ' git reset -q --hard && + test_when_finished "rm -f .git/index.lock && git reset -q --hard" && i=0 && while test $i -lt 12000 do - echo "100644 $_z40 0 some-file-$i" + echo "100644 1234567890123456789012345678901234567890 0 some-file-$i" i=$(( $i + 1 )) done | git update-index --index-info && - git rm -n "some-file-*" | :; - test -f .git/index.lock - status=$? - rm -f .git/index.lock - git reset -q --hard - test "$status" != 0 + git rm -n "some-file-*" | : && + test_path_is_missing .git/index.lock ' test_expect_success 'rm removes subdirectories recursively' ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 8f272bce84..73194b2c3d 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -293,7 +293,7 @@ check_threading () { (git format-patch --stdout "$@"; echo $? > status.out) | # Prints everything between the Message-ID and In-Reply-To, # and replaces all Message-ID-lookalikes by a sequence number - "$PERL_PATH" -ne ' + perl -ne ' if (/^(message-id|references|in-reply-to)/i) { $printing = 1; } elsif (/^\S/) { diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 2e7d73f090..8a309795c9 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -177,7 +177,7 @@ test_expect_success 'no diff with -diff' ' git diff | grep Binary ' -echo NULZbetweenZwords | "$PERL_PATH" -pe 'y/Z/\000/' > file +echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file test_expect_success 'force diff with "diff"' ' echo >.gitattributes "file diff" && diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh index 36e2f075c9..3ccc237a8d 100755 --- a/t/t4029-diff-trailing-space.sh +++ b/t/t4029-diff-trailing-space.sh @@ -27,7 +27,7 @@ test_expect_success \ git config --bool diff.suppressBlankEmpty true && git diff f > actual && test_cmp exp actual && - "$PERL_PATH" -i.bak -p -e "s/^\$/ /" exp && + perl -i.bak -p -e "s/^\$/ /" exp && git config --bool diff.suppressBlankEmpty false && git diff f > actual && test_cmp exp actual && diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index b1b906b1bb..1b420e3b5f 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -23,10 +23,10 @@ test_expect_success 'setup' ' git commit -m "Initial Version" 2>/dev/null && git checkout -b binary && - "$PERL_PATH" -pe "y/x/\000/" <file1 >file3 && + perl -pe "y/x/\000/" <file1 >file3 && cat file3 >file4 && git add file2 && - "$PERL_PATH" -pe "y/\000/v/" <file3 >file1 && + perl -pe "y/\000/v/" <file3 >file1 && rm -f file2 && git update-index --add --remove file1 file2 file3 file4 && git commit -m "Second Version" && diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh index fca815392e..2298ece801 100755 --- a/t/t4116-apply-reverse.sh +++ b/t/t4116-apply-reverse.sh @@ -12,14 +12,14 @@ test_description='git apply in reverse test_expect_success setup ' for i in a b c d e f g h i j k l m n; do echo $i; done >file1 && - "$PERL_PATH" -pe "y/ijk/\\000\\001\\002/" <file1 >file2 && + perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 && git add file1 file2 && git commit -m initial && git tag initial && for i in a b c g h i J K L m o n p q; do echo $i; done >file1 && - "$PERL_PATH" -pe "y/mon/\\000\\001\\002/" <file1 >file2 && + perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 && git commit -a -m second && git tag second && diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 7f6666fcd3..076e7709d2 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -78,7 +78,7 @@ test_expect_success 'activate rerere, old style (conflicting merge)' ' test_might_fail git config --unset rerere.enabled && test_must_fail git merge first && - sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && grep "^=======\$" $rr/preimage && ! test -f $rr/postimage && @@ -91,7 +91,7 @@ test_expect_success 'rerere.enabled works, too' ' git reset --hard && test_must_fail git merge first && - sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && grep ^=======$ $rr/preimage ' @@ -101,7 +101,7 @@ test_expect_success 'set up rr-cache' ' git config rerere.enabled true && git reset --hard && test_must_fail git merge first && - sha1=$("$PERL_PATH" -pe "s/ .*//" .git/MERGE_RR) && + sha1=$(perl -pe "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 ' @@ -185,7 +185,7 @@ test_expect_success 'rerere updates postimage timestamp' ' test_expect_success 'rerere clear' ' rm $rr/postimage && - echo "$sha1 a1" | "$PERL_PATH" -pe "y/\012/\000/" >.git/MERGE_RR && + echo "$sha1 a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR && git rerere clear && ! test -d $rr ' diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 72300b5f24..d8f23f488e 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -46,4 +46,19 @@ test_expect_success 'git log HEAD -- :/' ' test_cmp expected actual ' +test_expect_success 'command line pathspec parsing for "git log"' ' + git reset --hard && + >a && + git add a && + git commit -m "add an empty a" --allow-empty && + echo 1 >a && + git commit -a -m "update a to 1" && + git checkout HEAD^ && + echo 2 >a && + git commit -a -m "update a to 2" && + test_must_fail git merge master && + git add a && + git log --merge -- a +' + test_done diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh index ec5099b83d..93c7c366cf 100755 --- a/t/t4212-log-corrupt.sh +++ b/t/t4212-log-corrupt.sh @@ -13,11 +13,16 @@ test_expect_success 'setup' ' git update-ref refs/heads/broken_email $(cat broken_email.hash) ' +test_expect_success 'fsck notices broken commit' ' + git fsck 2>actual && + test_i18ngrep invalid.author actual +' + test_expect_success 'git log with broken author email' ' { echo commit $(cat broken_email.hash) echo "Author: A U Thor <author@example.com>" - echo "Date: Thu Jan 1 00:00:00 1970 +0000" + echo "Date: Thu Apr 7 15:13:13 2005 -0700" echo echo " foo" } >expect.out && @@ -30,7 +35,7 @@ test_expect_success 'git log with broken author email' ' ' test_expect_success 'git log --format with broken author email' ' - echo "A U Thor+author@example.com+" >expect.out && + echo "A U Thor+author@example.com+Thu Apr 7 15:13:13 2005 -0700" >expect.out && : >expect.err && git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err && diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index a07c871797..20c1961515 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -13,9 +13,9 @@ TRASH=`pwd` test_expect_success \ 'setup' \ 'rm -f .git/index* && - "$PERL_PATH" -e "print \"a\" x 4096;" > a && - "$PERL_PATH" -e "print \"b\" x 4096;" > b && - "$PERL_PATH" -e "print \"c\" x 4096;" > c && + perl -e "print \"a\" x 4096;" > a && + perl -e "print \"b\" x 4096;" > b && + perl -e "print \"c\" x 4096;" > c && test-genrandom "seed a" 2097152 > a_big && test-genrandom "seed b" 2097152 > b_big && git update-index --add a a_big b b_big c && @@ -129,7 +129,7 @@ test_expect_success \ cd "$TRASH" test_expect_success 'compare delta flavors' ' - "$PERL_PATH" -e '\'' + perl -e '\'' defined($_ = -s $_) or die for @ARGV; exit 1 if $ARGV[0] <= $ARGV[1]; '\'' test-2-$packname_2.pack test-3-$packname_3.pack @@ -151,7 +151,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' test_expect_success \ 'use packed deltified (REF_DELTA) objects' \ @@ -166,7 +166,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' test_expect_success \ 'use packed deltified (OFS_DELTA) objects' \ @@ -181,7 +181,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' unset GIT_OBJECT_DIRECTORY @@ -195,9 +195,9 @@ test_expect_success 'survive missing objects/pack directory' ' rm -fr $GOP && git index-pack --stdin --keep=test <../test-3-${packname_3}.pack && test -f $GOP/pack-${packname_3}.pack && - test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack && + cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack && test -f $GOP/pack-${packname_3}.idx && - test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx && + cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx && test -f $GOP/pack-${packname_3}.keep ) ' diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh index 35926debe3..663b02bbb1 100755 --- a/t/t5303-pack-corruption-resilience.sh +++ b/t/t5303-pack-corruption-resilience.sh @@ -98,7 +98,7 @@ test_expect_success \ 'create_new_pack && git prune-packed && chmod +w ${pack}.pack && - "$PERL_PATH" -i.bak -pe "s/ base /abcdef/" ${pack}.pack && + perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack && test_must_fail git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -155,7 +155,7 @@ test_expect_success \ 'create_new_pack && git prune-packed && chmod +w ${pack}.pack && - "$PERL_PATH" -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack && + perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 8f6e3922dc..ac79dd915d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -160,9 +160,7 @@ cat >test/expect <<EOF * remote two Fetch URL: ../two Push URL: ../three - HEAD branch (remote HEAD is ambiguous, may be one of the following): - another - master + HEAD branch: master Local refs configured for 'git push': ahead forces to master (fast-forwardable) master pushes to another (up to date) @@ -262,16 +260,12 @@ test_expect_success 'set-head --auto' ' ) ' -cat >test/expect <<\EOF -error: Multiple remote HEAD branches. Please choose one explicitly with: - git remote set-head two another - git remote set-head two master -EOF - -test_expect_success 'set-head --auto fails w/multiple HEADs' ' +test_expect_success 'set-head --auto has no problem w/multiple HEADs' ' ( cd test && - test_must_fail git remote set-head --auto two >output 2>&1 && + git fetch two "refs/heads/*:refs/remotes/two/*" && + git remote set-head --auto two >output 2>&1 && + echo "two/HEAD set to master" >expect && test_i18ncmp expect output ) ' diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index 8196af19f6..afb439e09c 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -113,6 +113,10 @@ test_expect_success 'follow redirects (302)' ' git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t ' +test_expect_success 'redirects re-root further requests' ' + git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited +' + test_expect_success 'clone from password-protected repository' ' echo two >expect && set_askpass user@host && @@ -146,6 +150,13 @@ test_expect_success 'no-op half-auth fetch does not require a password' ' expect_askpass none ' +test_expect_success 'redirects send auth to new location' ' + set_askpass user@host && + git -c credential.useHttpPath=true \ + clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth && + expect_askpass both user@host auth/smart/repo.git +' + test_expect_success 'disable dumb http on server' ' git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ config http.getanyfile false @@ -224,7 +235,7 @@ test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' done | git fast-import --export-marks=marks && # now assign tags to all the dangling commits we created above - tag=$("$PERL_PATH" -e "print \"bla\" x 30") && + tag=$(perl -e "print \"bla\" x 30") && sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs ) ' diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index f01edffa3c..e06146835c 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -37,7 +37,7 @@ test_expect_success 'fetch changes via git protocol' ' test_cmp file clone/file ' -test_expect_failure 'remote detects correct HEAD' ' +test_expect_success 'remote detects correct HEAD' ' git push public master:other && (cd clone && git remote set-head -d origin && @@ -122,8 +122,7 @@ test_remote_error() fi test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output && - echo "fatal: remote error: $msg: /$repo" >expect && - test_cmp expect output + test_i18ngrep "fatal: remote error: $msg: /$repo" output && ret=$? chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" (exit $ret) diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 8f3cd44d51..1d1c8755ea 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -329,4 +329,15 @@ test_expect_success 'bracketed hostnames are still ssh' ' expect_ssh myhost:123 src ' +test_expect_success 'clone from a repository with two identical branches' ' + + ( + cd src && + git checkout -b another master + ) && + git clone src target-11 && + test "z$( cd target-11 && git symbolic-ref HEAD )" = zrefs/heads/another + +' + test_done diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index b10685af4e..15e3d6476c 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -48,4 +48,12 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' ' ! grep one output ' +test_expect_success 'rev-list A..B and rev-list ^A B are the same' ' + git commit --allow-empty -m another && + git tag -a -m "annotated" v1.0 && + git rev-list --objects ^v1.0^ v1.0 >expect && + git rev-list --objects v1.0^..v1.0 >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh index bbb0581f88..e51eb41f4b 100755 --- a/t/t6011-rev-list-with-bad-commit.sh +++ b/t/t6011-rev-list-with-bad-commit.sh @@ -37,7 +37,7 @@ test_expect_success 'verify number of revisions' \ test_expect_success 'corrupt second commit object' \ ' - "$PERL_PATH" -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack && + perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack && test_must_fail git fsck --full ' diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh index 892a537989..59fc2f06e0 100755 --- a/t/t6013-rev-list-reverse-parents.sh +++ b/t/t6013-rev-list-reverse-parents.sh @@ -25,7 +25,7 @@ test_expect_success 'set up --reverse example' ' test_expect_success '--reverse --parents --full-history combines correctly' ' git rev-list --parents --full-history master -- foo | - "$PERL_PATH" -e "print reverse <>" > expected && + perl -e "print reverse <>" > expected && git rev-list --reverse --parents --full-history master -- foo \ > actual && test_cmp actual expected @@ -33,7 +33,7 @@ test_expect_success '--reverse --parents --full-history combines correctly' ' test_expect_success '--boundary does too' ' git rev-list --boundary --parents --full-history master ^root -- foo | - "$PERL_PATH" -e "print reverse <>" > expected && + perl -e "print reverse <>" > expected && git rev-list --boundary --reverse --parents --full-history \ master ^root -- foo > actual && test_cmp actual expected diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 752f5cb7d0..da5fb6c917 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -58,6 +58,8 @@ test_atom head parent '' test_atom head numparent 0 test_atom head object '' test_atom head type '' +test_atom head '*objectname' '' +test_atom head '*objecttype' '' test_atom head author 'A U Thor <author@example.com> 1151939924 +0200' test_atom head authorname 'A U Thor' test_atom head authoremail '<author@example.com>' @@ -91,6 +93,8 @@ test_atom tag parent '' test_atom tag numparent '' test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581' test_atom tag type 'commit' +test_atom tag '*objectname' '67a36f10722846e891fbada1ba48ed035de75581' +test_atom tag '*objecttype' 'commit' test_atom tag author '' test_atom tag authorname '' test_atom tag authoremail '' diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index b1a63655f9..63194d819e 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -9,6 +9,11 @@ test_expect_success 'gc empty repository' ' git gc ' +test_expect_success 'gc does not leave behind pid file' ' + git gc && + test_path_is_missing .git/gc.pid +' + test_expect_success 'gc --gobbledegook' ' test_expect_code 129 git gc --nonsense 2>err && test_i18ngrep "[Uu]sage: git gc" err diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index d432f42bcb..b90e985a48 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -293,6 +293,32 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm git diff-files --quiet ' +test_expect_success 'git mv moves a submodule with a .git directory and .gitmodules' ' + rm -rf mod && + git reset --hard && + git submodule update && + entry="$(git ls-files --stage sub | cut -f 1)" && + ( + cd sub && + rm -f .git && + cp -a ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + mkdir mod && + git mv sub mod/sub && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + echo mod/sub >expected && + git config -f .gitmodules submodule.sub.path >actual && + test_cmp expected actual && + git update-index --refresh && + git diff-files --quiet +' + test_expect_success 'git mv moves a submodule with gitfile' ' rm -rf mod/sub && git reset --hard && diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh index 95fab20361..98b7d7b969 100755 --- a/t/t7105-reset-patch.sh +++ b/t/t7105-reset-patch.sh @@ -25,15 +25,17 @@ test_expect_success PERL 'saying "n" does nothing' ' ' test_expect_success PERL 'git reset -p' ' - (echo n; echo y) | git reset -p && + (echo n; echo y) | git reset -p >output && verify_state dir/foo work head && - verify_saved_state bar + verify_saved_state bar && + test_i18ngrep "Unstage" output ' test_expect_success PERL 'git reset -p HEAD^' ' - (echo n; echo y) | git reset -p HEAD^ && + (echo n; echo y) | git reset -p HEAD^ >output && verify_state dir/foo work parent && - verify_saved_state bar + verify_saved_state bar && + test_i18ngrep "Apply" output ' # The idea in the rest is that bar sorts first, so we always say 'y' diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh index af00ab4d88..0f95f00477 100755 --- a/t/t7106-reset-unborn-branch.sh +++ b/t/t7106-reset-unborn-branch.sh @@ -37,11 +37,12 @@ test_expect_success PERL 'reset -p' ' rm .git/index && git add a && echo y >yes && - git reset -p <yes && + git reset -p <yes >output && >expect && git ls-files >actual && - test_cmp expect actual + test_cmp expect actual && + test_i18ngrep "Unstage" output ' test_expect_success 'reset --soft is a no-op' ' diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index be93f10cf0..b64c9ed8e7 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -254,10 +254,6 @@ test_expect_success 'ensure "status --cached --recursive" preserves the --cached ) && git submodule status --cached --recursive -- nested1 > ../actual ) && - if test_have_prereq MINGW - then - dos2unix actual - fi && test_cmp expect actual ' diff --git a/t/t7508-status.sh b/t/t7508-status.sh index 6fb59f3293..c987b5ed65 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -994,7 +994,7 @@ test_expect_success 'status -s submodule summary (clean submodule)' ' test_expect_success 'status -z implies porcelain' ' git status --porcelain | - "$PERL_PATH" -pe "s/\012/\000/g" >expect && + perl -pe "s/\012/\000/g" >expect && git status -z >output && test_cmp expect output ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 2813aa9a61..3119c8c523 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -23,7 +23,6 @@ test_expect_success $PREREQ \ echo do echo " echo \"!\$a!\"" echo "done >commandline\$output" - test_have_prereq MINGW && echo "dos2unix commandline\$output" echo "cat > msgtxt\$output" ) >fake.sendmail && chmod +x ./fake.sendmail && diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 9a40f1e199..8cfdfe790f 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -29,7 +29,7 @@ fi compare_svn_head_with () { # extract just the log message and strip out committer info. # don't use --limit here since svn 1.1.x doesn't have it, - LC_ALL="$a_utf8_locale" svn log `git svn info --url` | "$PERL_PATH" -w -e ' + LC_ALL="$a_utf8_locale" svn log `git svn info --url` | perl -w -e ' use bytes; $/ = ("-"x72) . "\n"; my @x = <STDIN>; diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh index c17aa3186f..d60da63f7a 100755 --- a/t/t9137-git-svn-dcommit-clobber-series.sh +++ b/t/t9137-git-svn-dcommit-clobber-series.sh @@ -20,8 +20,8 @@ test_expect_success '(supposedly) non-conflicting change from SVN' ' test x"`sed -n -e 61p < file`" = x61 && svn_cmd co "$svnrepo" tmp && (cd tmp && - "$PERL_PATH" -i.bak -p -e "s/^58$/5588/" file && - "$PERL_PATH" -i.bak -p -e "s/^61$/6611/" file && + perl -i.bak -p -e "s/^58$/5588/" file && + perl -i.bak -p -e "s/^61$/6611/" file && poke file && test x"`sed -n -e 58p < file`" = x5588 && test x"`sed -n -e 61p < file`" = x6611 && @@ -40,8 +40,8 @@ test_expect_success 'some unrelated changes to git' " test_expect_success 'change file but in unrelated area' " test x\"\`sed -n -e 4p < file\`\" = x4 && test x\"\`sed -n -e 7p < file\`\" = x7 && - "$PERL_PATH" -i.bak -p -e 's/^4\$/4444/' file && - "$PERL_PATH" -i.bak -p -e 's/^7\$/7777/' file && + perl -i.bak -p -e 's/^4\$/4444/' file && + perl -i.bak -p -e 's/^7\$/7777/' file && test x\"\`sed -n -e 4p < file\`\" = x4444 && test x\"\`sed -n -e 7p < file\`\" = x7777 && git commit -m '4 => 4444, 7 => 7777' file && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 88fc407ed6..27263dfb80 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -12,7 +12,7 @@ test_description='test git fast-import utility' # This could be written as "head -c $1", but IRIX "head" does not # support the -c option. head_c () { - "$PERL_PATH" -e ' + perl -e ' my $len = $ARGV[1]; while ($len > 0) { my $s; diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 34c2d8f49a..2312dec8f0 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -429,7 +429,7 @@ test_expect_success 'fast-export quotes pathnames' ' --cacheinfo 100644 $blob "path with \\backslash" \ --cacheinfo 100644 $blob "path with space" && git commit -m addition && - git ls-files -z -s | "$PERL_PATH" -0pe "s{\\t}{$&subdir/}" >index && + git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index && git read-tree --empty && git update-index -z --index-info <index && git commit -m rename && diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 043138631b..3edc4086d8 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -20,7 +20,7 @@ then skip_all='skipping git-cvsserver tests, cvs not found' test_done fi -"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done } diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index 8c3db76301..5a4ed28e49 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -68,7 +68,7 @@ then skip_all='skipping git-cvsserver tests, perl not available' test_done fi -"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done } diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh index db69af2cff..1e266effff 100755 --- a/t/t9402-git-cvsserver-refs.sh +++ b/t/t9402-git-cvsserver-refs.sh @@ -76,7 +76,7 @@ then skip_all='skipping git-cvsserver tests, perl not available' test_done fi -"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done } diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index 435d896476..102c133112 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -11,7 +11,7 @@ if ! test_have_prereq PERL; then test_done fi -"$PERL_PATH" -MTest::More -e 0 2>/dev/null || { +perl -MTest::More -e 0 2>/dev/null || { skip_all="Perl Test::More unavailable, skipping test" test_done } @@ -55,6 +55,6 @@ test_external_has_tap=1 test_external_without_stderr \ 'Perl API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl + perl "$TEST_DIRECTORY"/t9700/test.pl test_done diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index 34fbc90005..8134ab439b 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -263,7 +263,7 @@ test_expect_success 'cope with rcs keyword expansion damage' ' git config git-p4.attemptRCSCleanup true && (cd "$cli" && p4_append_to_file kwfile1.c) && old_lines=$(wc -l <kwfile1.c) && - "$PERL_PATH" -n -i -e "print unless m/Revision:/" kwfile1.c && + perl -n -i -e "print unless m/Revision:/" kwfile1.c && new_lines=$(wc -l <kwfile1.c) && test $new_lines = $(($old_lines - 1)) && diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index a7e9aacbb2..2f79146e6c 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -76,11 +76,11 @@ test_decode_color () { } nul_to_q () { - "$PERL_PATH" -pe 'y/\000/Q/' + perl -pe 'y/\000/Q/' } q_to_nul () { - "$PERL_PATH" -pe 'y/Q/\000/' + perl -pe 'y/Q/\000/' } q_to_cr () { @@ -648,7 +648,7 @@ test_seq () { 2) ;; *) error "bug in the test script: not 1 or 2 parameters to test_seq" ;; esac - "$PERL_PATH" -le 'print for $ARGV[0]..$ARGV[1]' -- "$@" + perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@" } # This function can be used to schedule some commands to be run @@ -710,3 +710,73 @@ test_ln_s_add () { git update-index --add --cacheinfo 120000 $ln_s_obj "$2" fi } + +perl () { + command "$PERL_PATH" "$@" +} + +# The following mingw_* functions obey POSIX shell syntax, but are actually +# bash scripts, and are meant to be used only with bash on Windows. + +# A test_cmp function that treats LF and CRLF equal and avoids to fork +# diff when possible. +mingw_test_cmp () { + # Read text into shell variables and compare them. If the results + # are different, use regular diff to report the difference. + local test_cmp_a= test_cmp_b= + + # When text came from stdin (one argument is '-') we must feed it + # to diff. + local stdin_for_diff= + + # Since it is difficult to detect the difference between an + # empty input file and a failure to read the files, we go straight + # to diff if one of the inputs is empty. + if test -s "$1" && test -s "$2" + then + # regular case: both files non-empty + mingw_read_file_strip_cr_ test_cmp_a <"$1" + mingw_read_file_strip_cr_ test_cmp_b <"$2" + elif test -s "$1" && test "$2" = - + then + # read 2nd file from stdin + mingw_read_file_strip_cr_ test_cmp_a <"$1" + mingw_read_file_strip_cr_ test_cmp_b + stdin_for_diff='<<<"$test_cmp_b"' + elif test "$1" = - && test -s "$2" + then + # read 1st file from stdin + mingw_read_file_strip_cr_ test_cmp_a + mingw_read_file_strip_cr_ test_cmp_b <"$2" + stdin_for_diff='<<<"$test_cmp_a"' + fi + test -n "$test_cmp_a" && + test -n "$test_cmp_b" && + test "$test_cmp_a" = "$test_cmp_b" || + eval "diff -u \"\$@\" $stdin_for_diff" +} + +# $1 is the name of the shell variable to fill in +mingw_read_file_strip_cr_ () { + # Read line-wise using LF as the line separator + # and use IFS to strip CR. + local line + while : + do + if IFS=$'\r' read -r -d $'\n' line + then + # good + line=$line$'\n' + else + # we get here at EOF, but also if the last line + # was not terminated by LF; in the latter case, + # some text was read + if test -z "$line" + then + # EOF, really + break + fi + fi + eval "$1=\$$1\$line" + done +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 0fa7dfde7b..b25249ec4c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -205,29 +205,17 @@ do --valgrind-only=*) valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)') shift ;; - --valgrind-parallel=*) - valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)') - shift ;; - --valgrind-only-stride=*) - valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)') - shift ;; - --valgrind-only-offset=*) - valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)') - shift ;; --tee) shift ;; # was handled already --root=*) root=$(expr "z$1" : 'z[^=]*=\(.*\)') shift ;; - --statusprefix=*) - statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)') - shift ;; *) echo "error: unknown test option '$1'" >&2; exit 1 ;; esac done -if test -n "$valgrind_only" || test -n "$valgrind_only_stride" +if test -n "$valgrind_only" then test -z "$valgrind" && valgrind=memcheck test -z "$verbose" && verbose_only="$valgrind_only" @@ -325,16 +313,16 @@ trap 'die' EXIT . "$TEST_DIRECTORY/test-lib-functions.sh" # You are not expected to call test_ok_ and test_failure_ directly, use -# the text_expect_* functions instead. +# the test_expect_* functions instead. test_ok_ () { test_success=$(($test_success + 1)) - say_color "" "${statusprefix}ok $test_count - $@" + say_color "" "ok $test_count - $@" } test_failure_ () { test_failure=$(($test_failure + 1)) - say_color error "${statusprefix}not ok $test_count - $1" + say_color error "not ok $test_count - $1" shift echo "$@" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } @@ -342,12 +330,12 @@ test_failure_ () { test_known_broken_ok_ () { test_fixed=$(($test_fixed+1)) - say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished" + say_color error "ok $test_count - $@ # TODO known breakage vanished" } test_known_broken_failure_ () { test_broken=$(($test_broken+1)) - say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage" + say_color warn "not ok $test_count - $@ # TODO known breakage" } test_debug () { @@ -377,9 +365,7 @@ maybe_teardown_verbose () { last_verbose=t maybe_setup_verbose () { test -z "$verbose_only" && return - if match_pattern_list $test_count $verbose_only || - { test -n "$valgrind_only_stride" && - expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; } + if match_pattern_list $test_count $verbose_only then exec 4>&2 3>&1 # Emit a delimiting blank line when going from @@ -403,7 +389,7 @@ maybe_teardown_valgrind () { maybe_setup_valgrind () { test -z "$GIT_VALGRIND" && return - if test -z "$valgrind_only" && test -z "$valgrind_only_stride" + if test -z "$valgrind_only" then GIT_VALGRIND_ENABLED=t return @@ -412,10 +398,6 @@ maybe_setup_valgrind () { if match_pattern_list $test_count $valgrind_only then GIT_VALGRIND_ENABLED=t - elif test -n "$valgrind_only_stride" && - expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null - then - GIT_VALGRIND_ENABLED=t fi } @@ -477,8 +459,8 @@ test_skip () { of_prereq=" of $test_prereq" fi - say_color skip >&3 "${statusprefix}skipping test: $@" - say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})" + say_color skip >&3 "skipping test: $@" + say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})" : true ;; *) @@ -516,11 +498,11 @@ test_done () { if test "$test_fixed" != 0 then - say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)" + say_color error "# $test_fixed known breakage(s) vanished; please update test(s)" fi if test "$test_broken" != 0 then - say_color warn "${statusprefix}# still have $test_broken known breakage(s)" + say_color warn "# still have $test_broken known breakage(s)" fi if test "$test_broken" != 0 || test "$test_fixed" != 0 then @@ -543,9 +525,9 @@ test_done () { then if test $test_remaining -gt 0 then - say_color pass "${statusprefix}# passed all $msg" + say_color pass "# passed all $msg" fi - say "${statusprefix}1..$test_count$skip_all" + say "1..$test_count$skip_all" fi test -d "$remove_trash" && @@ -559,8 +541,8 @@ test_done () { *) if test $test_external_has_tap -eq 0 then - say_color error "${statusprefix}# failed $test_failure among $msg" - say "${statusprefix}1..$test_count" + say_color error "# failed $test_failure among $msg" + say "1..$test_count" fi exit 1 ;; @@ -568,9 +550,6 @@ test_done () { esac } - -# Set up a directory that we can put in PATH which redirects all git -# calls to 'valgrind git ...'. if test -n "$valgrind" then make_symlink () { @@ -618,42 +597,33 @@ then make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit } - # In the case of --valgrind-parallel, we only need to do the - # wrapping once, in the main script. The worker children all - # have $valgrind_only_stride set, so we can skip based on that. - if test -z "$valgrind_only_stride" - then - # override all git executables in TEST_DIRECTORY/.. - GIT_VALGRIND=$TEST_DIRECTORY/valgrind - mkdir -p "$GIT_VALGRIND"/bin - for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-* - do - make_valgrind_symlink $file - done - # special-case the mergetools loadables - make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools" - OLDIFS=$IFS - IFS=: - for path in $PATH + # override all git executables in TEST_DIRECTORY/.. + GIT_VALGRIND=$TEST_DIRECTORY/valgrind + mkdir -p "$GIT_VALGRIND"/bin + for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-* + do + make_valgrind_symlink $file + done + # special-case the mergetools loadables + make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools" + OLDIFS=$IFS + IFS=: + for path in $PATH + do + ls "$path"/git-* 2> /dev/null | + while read file do - ls "$path"/git-* 2> /dev/null | - while read file - do - make_valgrind_symlink "$file" - done + make_valgrind_symlink "$file" done - IFS=$OLDIFS - fi + done + IFS=$OLDIFS PATH=$GIT_VALGRIND/bin:$PATH GIT_EXEC_PATH=$GIT_VALGRIND/bin export GIT_VALGRIND GIT_VALGRIND_MODE="$valgrind" export GIT_VALGRIND_MODE GIT_VALGRIND_ENABLED=t - if test -n "$valgrind_only" || test -n "$valgrind_only_stride" - then - GIT_VALGRIND_ENABLED= - fi + test -n "$valgrind_only" && GIT_VALGRIND_ENABLED= export GIT_VALGRIND_ENABLED elif test -n "$GIT_TEST_INSTALLED" then @@ -730,41 +700,6 @@ then else mkdir -p "$TRASH_DIRECTORY" fi - -# Gross hack to spawn N sub-instances of the tests in parallel, and -# summarize the results. Note that if this is enabled, the script -# terminates at the end of this 'if' block. -if test -n "$valgrind_parallel" -then - for i in $(test_seq 1 $valgrind_parallel) - do - root="$TRASH_DIRECTORY/vgparallel-$i" - mkdir "$root" - TEST_OUTPUT_DIRECTORY="$root" \ - ${SHELL_PATH} "$0" \ - --root="$root" --statusprefix="[$i] " \ - --valgrind="$valgrind" \ - --valgrind-only-stride="$valgrind_parallel" \ - --valgrind-only-offset="$i" & - pids="$pids $!" - done - trap "kill $pids" INT TERM HUP - wait $pids - trap - INT TERM HUP - for i in $(test_seq 1 $valgrind_parallel) - do - root="$TRASH_DIRECTORY/vgparallel-$i" - eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts | - sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')" - test_count=$(expr $test_count + $inner_total) - test_success=$(expr $test_success + $inner_success) - test_fixed=$(expr $test_fixed + $inner_fixed) - test_broken=$(expr $test_broken + $inner_broken) - test_failure=$(expr $test_failure + $inner_failed) - done - test_done -fi - # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$TRASH_DIRECTORY" || exit 1 @@ -817,6 +752,7 @@ case $(uname -s) in test_set_prereq NOT_CYGWIN test_set_prereq SED_STRIPS_CR test_set_prereq GREP_STRIPS_CR + GIT_TEST_CMP=mingw_test_cmp ;; *CYGWIN*) test_set_prereq POSIXPERM diff --git a/upload-pack.c b/upload-pack.c index a6c54e06bb..43342ac161 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -689,6 +689,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag return 0; } +static void format_symref_info(struct strbuf *buf, struct string_list *symref) +{ + struct string_list_item *item; + + if (!symref->nr) + return; + for_each_string_list_item(item, symref) + strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util); +} + static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" @@ -697,35 +707,64 @@ 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, cb_data)) + if (mark_our_ref(refname, sha1, flag, NULL)) return 0; - if (capabilities) - packet_write(1, "%s %s%c%s%s%s agent=%s\n", + if (capabilities) { + struct strbuf symref_info = STRBUF_INIT; + + format_symref_info(&symref_info, cb_data); + packet_write(1, "%s %s%c%s%s%s%s agent=%s\n", sha1_to_hex(sha1), refname_nons, 0, capabilities, allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "", stateless_rpc ? " no-done" : "", + symref_info.buf, git_user_agent_sanitized()); - else + strbuf_release(&symref_info); + } else { packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons); + } capabilities = NULL; if (!peel_ref(refname, peeled)) packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons); return 0; } +static int find_symref(const char *refname, const unsigned char *sha1, int flag, + void *cb_data) +{ + const char *symref_target; + struct string_list_item *item; + unsigned char unused[20]; + + if ((flag & REF_ISSYMREF) == 0) + return 0; + symref_target = resolve_ref_unsafe(refname, unused, 0, &flag); + if (!symref_target || (flag & REF_ISSYMREF) == 0) + die("'%s' is a symref but it is not?", refname); + item = string_list_append(cb_data, refname); + item->util = xstrdup(symref_target); + return 0; +} + static void upload_pack(void) { + struct string_list symref = STRING_LIST_INIT_DUP; + + head_ref_namespaced(find_symref, &symref); + for_each_namespaced_ref(find_symref, &symref); + if (advertise_refs || !stateless_rpc) { reset_timeout(); - head_ref_namespaced(send_ref, NULL); - for_each_namespaced_ref(send_ref, NULL); + head_ref_namespaced(send_ref, &symref); + for_each_namespaced_ref(send_ref, &symref); packet_flush(1); } else { head_ref_namespaced(mark_our_ref, NULL); for_each_namespaced_ref(mark_our_ref, NULL); } + string_list_clear(&symref, 1); if (advertise_refs) return; diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 4d8645867e..4266ada23f 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -108,7 +108,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz, void *priv) { if (len > 0 && (isalpha((unsigned char)*rec) || /* identifier? */ - *rec == '_' || /* also identifier? */ + *rec == '_' || /* also identifier? */ *rec == '$')) { /* identifiers from VMS and other esoterico */ if (len > sz) len = sz; |