Merge branch 'js/tests-windows-port-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 1 Nov 2013 14:38:54 +0000 (07:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Nov 2013 14:38:54 +0000 (07:38 -0700)
* js/tests-windows-port-fix:
tests: undo special treatment of CRLF for Windows
Windows: a test_cmp that is agnostic to random LF <> CRLF conversions
t5300-pack-object: do not compare binary data using test_cmp

70 files changed:
Documentation/Makefile
Documentation/RelNotes/1.8.4.2.txt
Documentation/RelNotes/1.8.5.txt
Documentation/git-repack.txt
Documentation/git-svn.txt
Documentation/git.txt
Documentation/gitk.txt
Documentation/howto/recover-corrupted-object-harder.txt [new file with mode: 0644]
GIT-VERSION-GEN
abspath.c
alias.c
alloc.c
archive.c
base85.c
block-sha1/sha1.c
builtin/checkout-index.c
builtin/checkout.c
builtin/for-each-ref.c
builtin/gc.c
builtin/mv.c
builtin/read-tree.c
builtin/reset.c
builtin/rev-list.c
builtin/symbolic-ref.c
cache.h
compat/regex/regcomp.c
connect.c
connect.h
contrib/completion/git-prompt.sh
contrib/remote-helpers/git-remote-hg
date.c
entry.c
fetch-pack.c
git-add--interactive.perl
graph.c
http-push.c
http.c
http.h
ident.c
line-log.c
path.c
refs.c
remote-curl.c
remote.c
revision.c
send-pack.c
setup.c
sha1_file.c
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/t0060-path-utils.sh
t/t2010-checkout-ambiguous.sh
t/t2024-checkout-dwim.sh
t/t3200-branch.sh
t/t3600-rm.sh
t/t4208-log-magic-pathspec.sh
t/t4212-log-corrupt.sh
t/t5505-remote.sh
t/t5551-http-fetch.sh
t/t5570-git-daemon.sh
t/t5601-clone.sh
t/t6000-rev-list-misc.sh
t/t6300-for-each-ref.sh
t/t6500-gc.sh
t/t7001-mv.sh
t/t7105-reset-patch.sh
t/t7106-reset-unborn-branch.sh
t/test-lib.sh
upload-pack.c
xdiff/xemit.c
index 4f13a23893047a022be23d245d2df920b1845eac..91a12c7e51e7869f4b64385aeffc1762c3ddfa38 100644 (file)
@@ -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
index 867ae69070d01dae59352d6d5e684a51288b026c..9adccb1efb8d3fe8a117fdd13b40abb1cb757fb7 100644 (file)
@@ -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.
index 4c2aa7077b09731c120ce398036df2ed42364298..d940c033bd70505af1af2d99b058ad41f0741231 100644 (file)
@@ -205,6 +205,8 @@ UI, Workflows & Features
 
 Performance, Internal Implementation, etc.
 
+ * 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 +244,42 @@ 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).
 
+ * 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.
index 4c1aff65e62f00db236ccb4f48b6071fc50891f7..509cf73e50542758d0ef3930f56fdb5dd0183aac 100644 (file)
@@ -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.
 
index 2a3847649df540242828ac1fbae4b962ff75ce94..dcad890616c818ee45b42b5abedcaf6e67fd5c60 100644 (file)
@@ -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
index 4f7e07f2e0f4f6317c81467f7f880b4113b12de1..824a179a965ba419492840afb55d1d55e038ed0e 100644 (file)
@@ -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].
 
index c17e760184fad6425276acfac7ade83ab13328cf..d44e14c13850a1b98cae914357cc7eed7676e65f 100644 (file)
@@ -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 (file)
index 0000000..6f33dac
--- /dev/null
@@ -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).
index b444c18f176cb33a80a1378586d6fa54bccd112a..4dcaac3e9798b9c1fb2f1575408303448d7197d6 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.4.GIT
+DEF_VER=v1.8.5-rc0
 
 LF='
 '
index 64adbe2a1b5e5679fb0dc772452b4aca1865ee4c..e390994abff32054452e878484e79c249474389f 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -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);
        }
diff --git a/alias.c b/alias.c
index eb9f08b912b2089d434926fc6083ac5ab90c73bc..9938f03c255029ee8f5b10623e5e7bb65f2d7921 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -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];
 }
diff --git a/alloc.c b/alloc.c
index aeae55c976802264d714282218e58a858a9c68b5..f3ee745695643c00f175288a9592ab073dd589b0 100644 (file)
--- a/alloc.c
+++ b/alloc.c
@@ -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)
 {
index 99fadc88d04ea077e5e5d6e6c065f7c54a4eddaf..346f3b2f1ab0d522638475b729274d992d5b82d5 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -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);
 }
index 781b5754f0e533008694e71ac7cfe2e52b2d0ac6..5ca601ee14fd2ab3b78577aa22a5db778bc7fbe0 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -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;
index a8d4bf9301d25de60c02718b9d446edf5c665184..e1a1eb609754a745da956a6994d23aec3c4c9c05 100644 (file)
@@ -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]);
 }
index 69e167b16c284534d84019ba0f781fe2c3a889dd..61e75eb60c992e38ddf195abad564a76cbf934cb 100644 (file)
@@ -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;
 
index 0f573970372664a16024e83ed9733c94eeee3b50..54f80bd38a7413390ed2093a626b129a1a3e8e7f 100644 (file)
@@ -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
index 1d4083c2ddef07c6488d36720e6a0c48b04c6585..d096051b15c14a4f109bc9a9cfcf9749c14258b9 100644 (file)
@@ -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;
 
index 891a2c2ecb046dc29b2d9ce8b9d2982084bf9b19..c14190f840b0427820ebf69b209d86f0a21c62b7 100644 (file)
@@ -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;
 }
 
index aec79d18386b52a943b20e6ebe0dfc9b6f074f0f..2e0e61b6517d74edc8b7ef2e56c75927385e7c85 100644 (file)
@@ -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)
index 0f5d7fe23f5b1e15e24e93daed68ece7542cf104..0d7ef847a70581214fb88c3a981581670f7decbe 100644 (file)
@@ -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))
index f2f9d55392d2193432d5a572a70cd27afcf6b136..60048030dd4d05e9938f298002d02afc31528bf1 100644 (file)
@@ -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
index 4fc16166374b86927e605ccbfe341aebe0e3240f..0745e2d05330d21152f2aff720d0de338270aa6d 100644 (file)
@@ -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);
index f48195942123692c95ad8e5585f853ad54a3dbe4..71286b4fae4792dce73ab1533204e803e2a61334 100644 (file)
@@ -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) {
diff --git a/cache.h b/cache.h
index 5e3fc72fd40f49638c24c632121810468fb3acea..9aa46e70c3faf7eb678eeef7b825ae52fb4bc0bf 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -751,6 +751,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 +976,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 {
index b2c5d465acd2bd37c1d316353eae544e564fc334..06f30887080bc293994ed470ff9b313ae048dcfb 100644 (file)
@@ -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));
index 40868610ab1c02d10cc7cdc927fdf97a5e010414..06e88b0705f7fbd138e504920c20c70218121696 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -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;
 
index 9dff25cad4de82e84343b50490b1b1601103ffac..64fb7dbbee8714c034c5e018b3b1b4f8c29f0ea7 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -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
index 202e2e520f42059c308f196e0c5022c3dbb4a142..7b732d2aeba0e3bca3b42d28f2f8f5730a08ccee 100644 (file)
@@ -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
index 92d994e470f05db8536ba443a6afb192be8c2452..3222afd9da865087b75efbb1be6ea36e240cb452 100755 (executable)
@@ -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/date.c b/date.c
index 29f15404feab0d98f377b312a3fcf5d8888fdb0a..83b4166344b31ea615603acaeb95a873f2c05077 100644 (file)
--- a/date.c
+++ b/date.c
@@ -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++) {
diff --git a/entry.c b/entry.c
index acc892f9a62b5bc3d3442b044b29bf8d763bf3ba..7b7aa8167adade20f38cfb2709e02b3ed31f11a4 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -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);
index a0e0350ae6bdf338b3c11c21fc050edb60c88569..1042448fa0b321e8826f0fdb69c89810c731cf5d 100644 (file)
@@ -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 =
index 51563840f49089cf250992fd47fe70a45cef0c52..24bb1ab9929d2cbf117051ed0e4edeb37b0efaea 100755 (executable)
@@ -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/graph.c b/graph.c
index b24d04c4066dd4d09200bb27d09c91d93c1bb5b0..640433166b39c515746d698566839326d9ce7bbe 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -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;
 }
index 69200baf7651c2bcb99d6bc2068429e22865f900..34cb70f90ea14a18848bd270463f5a34dc2c8fd3 100644 (file)
@@ -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);
diff --git a/http.c b/http.c
index f3e1439d58a00328ece38a6eead63309fe7f58ca..bcf54aa35f96e8f0b87b6e2fd23408991a684b66 100644 (file)
--- a/http.c
+++ b/http.c
@@ -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 @@ static int http_get_file(const char *url, const char *filename, int options)
 
 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 @@ static int fetch_and_setup_pack_index(struct packed_git **packs_head,
 
 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;
 
diff --git a/http.h b/http.h
index d77c1b54f24d142461b9e1a0d11cf6de041bc59f..12ca5c892d61a063c856bcdef9154a103c0aa4bc 100644 (file)
--- a/http.h
+++ b/http.h
@@ -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);
 
diff --git a/ident.c b/ident.c
index b29f81f83a68258b354c59df9bde40c254060ed9..1d9b6e770d02d12f77b417e05aca5134eccf1c40 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -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;
index 8b6e497b3ff187ee328cba395f30d3db7f86c0b4..717638b333b680da7a3a5acefcb73db9d956a00f 100644 (file)
@@ -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);
        }
 }
diff --git a/path.c b/path.c
index 9fd28bcd080ef0a64f0b8d8a5d9d680a741d75c3..24594c41120bebab1e04f5bc4bd8fc484ea71b97 100644 (file)
--- a/path.c
+++ b/path.c
@@ -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]))
@@ -530,6 +550,51 @@ const char *relative_path(const char *in, const char *prefix,
        return sb->buf;
 }
 
+/*
+ * 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.
  *
diff --git a/refs.c b/refs.c
index 3710748ab88d8fa4639c3a0a436ca0e2b0c25552..0c0e963532a6dcf2f9c062b371d40a2cd10571c3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3376,7 +3376,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]);
 
index b5ebe0180069c1c1f96b769a37c5f3eb9459dfe9..c9b891adbf134193f07681a631ae368671e04aa6 100644 (file)
@@ -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 @@ static int post_rpc(struct rpc_state *rpc)
        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) {
index e9fedfa918d6806532184b8735d2761951fd4a2e..9f1a8aa2c499ae5d2eb8f76f8218eb62937eea51 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -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) &&
index 0173e0148b850bd1a3e2e7e5c652050ade6d5ba4..3fdea51ffed4839c6c30f34b45d67f5e95ab6cc2 100644 (file)
@@ -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 = '.';
index b228d65613c511ffa59eb3ab254ee2e2b6f26c9b..fab62e3da05913b5a4f76db7ed1c17e5831384d2 100644 (file)
@@ -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);
diff --git a/setup.c b/setup.c
index f08dd649761f4d53abef66da052340a3c78d0fe5..dbf41387204766981ebda444bc56a0884f9341f5 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -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)
index f80bbe467437a4ffc161a4cdd09f97cf5436d4f9..7dadd04cb75a9f681cab17f616600cf437fe82ba 100644 (file)
@@ -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;
index 54dbbfe5ceeb9bf91cdad3393c2fdfe95c06fd18..ad8f1ef71efcd7e449d2cad8edd3cf80c98ca188 100644 (file)
@@ -204,7 +204,8 @@ set_askpass() {
 }
 
 expect_askpass() {
-       dest=$HTTPD_DEST
+       dest=$HTTPD_DEST${3+/$3}
+
        {
                case "$1" in
                none)
index 397c4804010c7566a4f969822c413e46fe817e53..3a03e8263db7e1c632bf0552ec94ae6834de4bf9 100644 (file)
@@ -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
index 2bd5e3274549c7a50375e90e58fa5a4dc393491d..07c10c8dca80b9d289431a72d5bf120ca442113d 100755 (executable)
@@ -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
index 7cc0a3582ef1bb1598bc5dfc5334bfbe006c3f5a..87bdf9c96bfacf4ac921ad9b195db46d9c3b079a 100755 (executable)
@@ -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
index 094b92ef489156980dc1429136d5dad25d01ac7d..6ecb559465b43e528a5cb1511b758cc48d940e92 100755 (executable)
@@ -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
index 0fe7647928b9d20963e60e56009f72c54ee488e2..fcdb867748f3c651fdb58c148016f3da1cf2c9be 100755 (executable)
@@ -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
 '
 
index 639cb70941078cbf10fb0653ac34dd511941f909..540c49bab618ffb75dd789b098ef982634648f7c 100755 (executable)
@@ -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' '
index 72300b5f244504540a551854cb82f81cf5391a55..d8f23f488e00dad97eeb5d22f5f0db01bcab8021 100755 (executable)
@@ -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
index ec5099b83d7490184a68691ff2ca0e875cfab209..93c7c366cfeffa46cc72665f6ce676bd37c3c2fe 100755 (executable)
@@ -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 &&
index 8f6e3922dce2b676221610d83a87c5b36d3bc5b6..ac79dd915da4a520e90fca6a11a239aba6e688d7 100755 (executable)
@@ -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
        )
 '
index 8196af19f68ac3772a81326828f7cdf83ae399a1..fb16833f7673ed33cd097b98e4ca91df1a89b135 100755 (executable)
@@ -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
index f01edffa3c0babf76593fbd211f6606ff2036c74..e06146835cdda5b7264a34a24405aedd9f950006 100755 (executable)
@@ -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)
index 8f3cd44d514f3996b62da28f6f83be7b1d786f01..1d1c8755ead4e432744a82f0d7b8ed7dfc8a6510 100755 (executable)
@@ -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
index b10685af4e3e19c79e1e656aadc9aae3430777f0..15e3d6476c78ba3447504aa133e9e1d7ffbfd87d 100755 (executable)
@@ -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
index 752f5cb7d0f522a8a6654bae06b07a1727df7505..da5fb6c9170b3e7fd818a59fc6e48ff3161d12da 100755 (executable)
@@ -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 ''
index b1a63655f98dfc8d19c1a80f86652654cd9cade6..63194d819efe07af241573662c660e6e92b503f5 100755 (executable)
@@ -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
index d432f42bcbd509b0dae46848d2745eba26fc6872..b90e985a48efbf8cdb16c6166356f6984ffbcea8 100755 (executable)
@@ -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 &&
index 95fab2036109c5a4aef8ff3d8100403fe335819f..98b7d7b969e4eb3ded40aba1f12d2afc8bad849a 100755 (executable)
@@ -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'
index af00ab4d88cc75ec720e31da3a0d5d3a1ef8df40..0f95f004777ac3db191264fc5760c17bc1bdf77b 100755 (executable)
@@ -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' '
index 77e487bd576f63564658cef3a7d8aeabe6e08b13..b25249ec4cc2844f5cd7491dd5fcc88c884ff450 100644 (file)
@@ -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,17 +389,13 @@ 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
        fi
        GIT_VALGRIND_ENABLED=
        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
index a6c54e06bb4c4725ace576740912cbb6133e291e..43342ac161952f5c03c7b737231c4c664b7513a7 100644 (file)
@@ -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;
 
index 4d8645867e7d0103d49882877a7e4a8abacd24ea..4266ada23f3c37c1989c7fa4283b423d16c0891b 100644 (file)
@@ -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;