Code

Merge branch 'ne/pack-local-doc' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 8 Mar 2010 08:36:00 +0000 (00:36 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 8 Mar 2010 08:36:00 +0000 (00:36 -0800)
* ne/pack-local-doc:
  pack-objects documentation: Fix --honor-pack-keep as well.
  pack-objects documentation: reword "objects that appear in the standard input"
  Documentation: pack-objects: Clarify --local's semantics.

73 files changed:
Documentation/RelNotes-1.7.0.1.txt
Documentation/RelNotes-1.7.0.2.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-fast-import.txt
Documentation/git-grep.txt
Documentation/git-var.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/technical/api-run-command.txt
GIT-VERSION-GEN
Makefile
RelNotes
advice.c
advice.h
bisect.c
builtin-checkout.c
builtin-commit.c
builtin-fetch-pack.c
builtin-grep.c
builtin-mailinfo.c
builtin-pack-objects.c
builtin-receive-pack.c
builtin-rev-list.c
builtin-send-pack.c
builtin-shortlog.c
builtin-var.c
cache.h
compat/mkstemps.c [deleted file]
convert.c
diff.c
fast-import.c
git-add--interactive.perl
git-am.sh
git-sh-setup.sh
git-stash.sh
git-svn.perl
gitweb/README
gitweb/gitweb.perl
grep.c
grep.h
imap-send.c
pager.c
path.c
remote-curl.c
rerere.c
revision.c
run-command.c
run-command.h
sha1_file.c
sha1_name.c
t/lib-patch-mode.sh [changed mode: 0755->0644]
t/t1304-default-acl.sh [new file with mode: 0755]
t/t2016-checkout-patch.sh
t/t3301-notes.sh
t/t4017-diff-retval.sh
t/t5100/msg0015
t/t5401-update-hooks.sh
t/t6000lib.sh [changed mode: 0755->0644]
t/t6030-bisect-porcelain.sh
t/t7002-grep.sh
t/t7006-pager.sh [new file with mode: 0755]
t/t7006/test-terminal.perl [new file with mode: 0755]
t/t7201-co.sh
t/t7406-submodule-update.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
transport.c
transport.h
upload-pack.c
utf8.c
utf8.h
wrapper.c

index 970cd59330b67d3ddb7e7016c949e9c0a216517f..8ff5bcada81f47f321eb6b7cb3a67963fe92a088 100644 (file)
@@ -7,9 +7,17 @@ Fixes since v1.7.0
  * In a freshly created repository "rev-parse HEAD^0" complained that
    it is dangling symref, even though "rev-parse HEAD" didn't.
 
+ * "git show :no-such-name" tried to access the index without bounds
+   check, leading to a potential segfault.
+
  * Message from "git cherry-pick" was harder to read and use than necessary
    when it stopped due to conflicting changes.
 
+ * We referred to ".git/refs/" throughout the documentation when we
+   meant to talk about abstract notion of "ref namespace".  Because
+   people's repositories often have packed refs these days, this was
+   confusing.
+
  * "git diff --output=/path/that/cannot/be/written" did not correctly
    error out.
 
@@ -24,8 +32,4 @@ Fixes since v1.7.0
    option was propagated to "git stash drop" that is internally run at the
    end.
 
---
-exec >/var/tmp/1
-echo O=$(git describe)
-O=v1.7.0-22-gc69f921
-git shortlog $O..
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.0.2.txt b/Documentation/RelNotes-1.7.0.2.txt
new file mode 100644 (file)
index 0000000..fcb46ca
--- /dev/null
@@ -0,0 +1,40 @@
+Git v1.7.0.2 Release Notes
+==========================
+
+Fixes since v1.7.0.1
+--------------------
+
+ * GIT_PAGER was not honored consistently by some scripted Porcelains, most
+   notably "git am".
+
+ * updating working tree files after telling git to add them to the
+   index and while it is still working created garbage object files in
+   the repository without diagnosing it as an error.
+
+ * "git bisect -- pathspec..." did not diagnose an error condition properly when
+   the simplification with given pathspec made the history empty.
+
+ * "git rev-list --cherry-pick A...B" now has an obvious optimization when the
+   histories haven't diverged (i.e. when one end is an ancestor of the other).
+
+ * "git diff --quiet -w" did not work as expected.
+
+ * "git fast-import" didn't work with a large input, as it lacked support
+   for producing the pack index in v2 format.
+
+ * "git imap-send" didn't use CRLF line endings over the imap protocol
+   when storing its payload to the draft box, violating RFC 3501.
+
+ * "git log --format='%w(x,y,z)%b'" and friends that rewrap message
+   has been optimized for utf-8 payload.
+
+ * Error messages generated on the receiving end did not come back to "git
+   push".
+
+ * "git status" in 1.7.0 lacked the optimization we used to have in 1.6.X series
+   to speed up scanning of large working tree.
+
+ * "gitweb" did not diagnose parsing errors properly while reading tis configuration
+   file.
+
+And other minor fixes and documentation updates.
index 4c36aa95b7d3fc0d456d42bbdfdf5a118931094b..7103172ed30c33f2c7933f530d03ce98c1e4f360 100644 (file)
@@ -138,6 +138,11 @@ advice.*::
                Advice on how to set your identity configuration when
                your information is guessed from the system username and
                domain name. Default: true.
+
+       detachedHead::
+               Advice shown when you used linkgit::git-checkout[1] to
+               move to the detach HEAD state, to instruct how to create
+               a local branch after the fact.  Default: true.
 --
 
 core.fileMode::
@@ -680,9 +685,7 @@ color.grep::
 
 color.grep.match::
        Use customized color for matches.  The value of this variable
-       may be specified as in color.branch.<slot>.  It is passed using
-       the environment variables 'GREP_COLOR' and 'GREP_COLORS' when
-       calling an external 'grep'.
+       may be specified as in color.branch.<slot>.
 
 color.interactive::
        When set to `always`, always use colors for interactive prompts
index c66c565bbe3d0e25ded50786e5503b749ad72163..23864df8da0694a0daefb81fe629e22a1deb06f1 100644 (file)
@@ -44,7 +44,7 @@ OPTIONS
        Remove everything in body before a scissors line (see
        linkgit:git-mailinfo[1]).
 
----no-scissors::
+--no-scissors::
        Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
 -q::
index 6764ff188688070bfea379de7dccc13a1557cb20..19082b04eb7ec77bcf974e0c76b8d20bb736c021 100644 (file)
@@ -45,10 +45,7 @@ OPTIONS
 
 --max-pack-size=<n>::
        Maximum size of each output packfile.
-       The default is 4 GiB as that is the maximum allowed
-       packfile size (due to file format limitations). Some
-       importers may wish to lower this, such as to ensure the
-       resulting packfiles fit on CDs.
+       The default is unlimited.
 
 --big-file-threshold=<n>::
        Maximum size of a blob that fast-import will attempt to
index e019e760b4b4d58dfbe8819941947008319aedac..c44724d03a60640d34963f068149a3fe6803d20e 100644 (file)
@@ -22,12 +22,12 @@ SYNOPSIS
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern>
           [--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
-          [--] [<path>...]
+          [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
-Look for specified patterns in the working tree files, blobs
-registered in the index file, or given tree objects.
+Look for specified patterns in the tracked files in the work tree, blobs
+registered in the index file, or blobs in given tree objects.
 
 
 OPTIONS
@@ -49,7 +49,7 @@ OPTIONS
        Don't match the pattern in binary files.
 
 --max-depth <depth>::
-       For each pathspec given on command line, descend at most <depth>
+       For each <pathspec> given on command line, descend at most <depth>
        levels of directories. A negative value means no limit.
 
 -w::
@@ -168,12 +168,19 @@ OPTIONS
 
 \--::
        Signals the end of options; the rest of the parameters
-       are <path> limiters.
+       are <pathspec> limiters.
 
+<pathspec>...::
+       If given, limit the search to paths matching at least one pattern.
+       Both leading paths match and glob(7) patterns are supported.
 
 Example
 -------
 
+git grep 'time_t' -- '*.[ch]'::
+       Looks for `time_t` in all tracked .c and .h files in the working
+       directory and its subdirectories.
+
 git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
index bb981822a470b2df6c79156db8aa94569869e43e..458f3e2755fcc6431757af6178942ef756f6a5bd 100644 (file)
@@ -8,7 +8,7 @@ git-var - Show a git logical variable
 
 SYNOPSIS
 --------
-'git var' [ -l | <variable> ]
+'git var' ( -l | <variable> )
 
 DESCRIPTION
 -----------
index 01c463101b9b212f002e10a0d9e89a2df12652a2..35c0c7983d2c18bc0701bc5d6ec22bebc53f792f 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.0/git.html[documentation for release 1.7.0]
+* link:v1.7.0.2/git.html[documentation for release 1.7.0.2]
 
 * release notes for
+  link:RelNotes-1.7.0.2.txt[1.7.0.2],
+  link:RelNotes-1.7.0.1.txt[1.7.0.1],
   link:RelNotes-1.7.0.txt[1.7.0].
 
 * link:v1.6.6.2/git.html[documentation for release 1.6.6.2]
@@ -229,7 +231,10 @@ help ...`.
 
 -p::
 --paginate::
-       Pipe all output into 'less' (or if set, $PAGER).
+       Pipe all output into 'less' (or if set, $PAGER) if standard
+       output is a terminal.  This overrides the `pager.<cmd>`
+       configuration options (see the "Configuration Mechanism" section
+       below).
 
 --no-pager::
        Do not pipe git output into a pager.
@@ -401,7 +406,8 @@ people.  Here is an example:
 ------------
 
 Various commands read from the configuration file and adjust
-their operation accordingly.
+their operation accordingly.  See linkgit:git-config[1] for a
+list.
 
 
 Identifier Terminology
index b396a871b32de52962eaa36f9d6be19d108d88ee..d892e642eded92fd0409e39fc34c005f88bc4a9b 100644 (file)
@@ -511,7 +511,8 @@ command to run to merge ancestor's version (`%O`), current
 version (`%A`) and the other branches' version (`%B`).  These
 three tokens are replaced with the names of temporary files that
 hold the contents of these versions when the command line is
-built.
+built. Additionally, %L will be replaced with the conflict marker
+size (see below).
 
 The merge driver is expected to leave the result of the merge in
 the file named with `%A` by overwriting it, and exit with zero
index 68bf4cad8b0298349b6cc67e822a4d60e1ff1d24..44876fa703578f4952d6e928993e15ddec70439c 100644 (file)
@@ -64,8 +64,8 @@ The functions above do the following:
 `start_async`::
 
        Run a function asynchronously. Takes a pointer to a `struct
-       async` that specifies the details and returns a pipe FD
-       from which the caller reads. See below for details.
+       async` that specifies the details and returns a set of pipe FDs
+       for communication with the function. See below for details.
 
 `finish_async`::
 
@@ -135,7 +135,7 @@ stderr as follows:
 
        .in: The FD must be readable; it becomes child's stdin.
        .out: The FD must be writable; it becomes child's stdout.
-       .err > 0 is not supported.
+       .err: The FD must be writable; it becomes child's stderr.
 
   The specified FD is closed by start_command(), even if it fails to
   run the sub-process!
@@ -180,17 +180,47 @@ The caller:
    struct async variable;
 2. initializes .proc and .data;
 3. calls start_async();
-4. processes the data by reading from the fd in .out;
-5. closes .out;
+4. processes communicates with proc through .in and .out;
+5. closes .in and .out;
 6. calls finish_async().
 
+The members .in, .out are used to provide a set of fd's for
+communication between the caller and the callee as follows:
+
+. Specify 0 to have no file descriptor passed.  The callee will
+  receive -1 in the corresponding argument.
+
+. Specify < 0 to have a pipe allocated; start_async() replaces
+  with the pipe FD in the following way:
+
+       .in: Returns the writable pipe end into which the caller
+       writes; the readable end of the pipe becomes the function's
+       in argument.
+
+       .out: Returns the readable pipe end from which the caller
+       reads; the writable end of the pipe becomes the function's
+       out argument.
+
+  The caller of start_async() must close the returned FDs after it
+  has completed reading from/writing from them.
+
+. Specify a file descriptor > 0 to be used by the function:
+
+       .in: The FD must be readable; it becomes the function's in.
+       .out: The FD must be writable; it becomes the function's out.
+
+  The specified FD is closed by start_async(), even if it fails to
+  run the function.
+
 The function pointer in .proc has the following signature:
 
-       int proc(int fd, void *data);
+       int proc(int in, int out, void *data);
 
-. fd specifies a writable file descriptor to which the function must
-  write the data that it produces. The function *must* close this
-  descriptor before it returns.
+. in, out specifies a set of file descriptors to which the function
+  must read/write the data that it needs/produces.  The function
+  *must* close these descriptors before it returns.  A descriptor
+  may be -1 if the caller did not configure a descriptor for that
+  direction.
 
 . data is the value that the caller has specified in the .data member
   of struct async.
@@ -205,8 +235,8 @@ because this facility is implemented by a pipe to a forked process on
 UNIX, but by a thread in the same address space on Windows:
 
 . It cannot change the program's state (global variables, environment,
-  etc.) in a way that the caller notices; in other words, .out is the
-  only communication channel to the caller.
+  etc.) in a way that the caller notices; in other words, .in and .out
+  are the only communication channels to the caller.
 
 . It must not change the program's state that the caller of the
   facility also uses.
index a668143d7a42dd6323d73b9acee62ac7a992f7ea..d968ff0fcd89176ab0e3e3866d3f30c172a76133 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.0.GIT
+DEF_VER=v1.7.0.2
 
 LF='
 '
index 7bf2fca4070d2d00ac31d8b4dca6dff19b79cc79..4387d4207f49d4b80e8e922ccc85bf157447aa0b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1200,7 +1200,6 @@ ifdef NO_MKDTEMP
 endif
 ifdef NO_MKSTEMPS
        COMPAT_CFLAGS += -DNO_MKSTEMPS
-       COMPAT_OBJS += compat/mkstemps.o
 endif
 ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
index 9fadb0afe4ae9bac23f80dd2b64106a171720ec9..8cf972cf485bbc26c479a6ac07f3005b1d188c84 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.0.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.7.0.2.txt
\ No newline at end of file
index 936d98ba2ba2c597f69188c8e7f9f1abcc7169a8..0be4b5f008e1646946ba12fa5e51aa0830911ece 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -5,6 +5,7 @@ int advice_status_hints = 1;
 int advice_commit_before_merge = 1;
 int advice_resolve_conflict = 1;
 int advice_implicit_identity = 1;
+int advice_detached_head = 1;
 
 static struct {
        const char *name;
@@ -15,6 +16,7 @@ static struct {
        { "commitbeforemerge", &advice_commit_before_merge },
        { "resolveconflict", &advice_resolve_conflict },
        { "implicitidentity", &advice_implicit_identity },
+       { "detachedhead", &advice_detached_head },
 };
 
 int git_default_advice_config(const char *var, const char *value)
index 9b7a3ad1ca126cb882e87d400820043043061f78..3244ebb5c1caffebaaf8f28ce221533c38ec29b5 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -8,6 +8,7 @@ extern int advice_status_hints;
 extern int advice_commit_before_merge;
 extern int advice_resolve_conflict;
 extern int advice_implicit_identity;
+extern int advice_detached_head;
 
 int git_default_advice_config(const char *var, const char *value);
 
index 6dc27ee7a6090e56d5b0f2072a72553d3b3e3b87..b556b11610480afd80cddd86a81af9737254ee36 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -986,6 +986,12 @@ int bisect_next_all(const char *prefix)
                exit(1);
        }
 
+       if (!all) {
+               fprintf(stderr, "No testable commit found.\n"
+                       "Maybe you started with bad path parameters?\n");
+               exit(4);
+       }
+
        bisect_rev = revs.commits->item->object.sha1;
        memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41);
 
index 527781728e0706b906a94ddfb8ee2e8bb06fa05e..c5ab7835e1fe260b9ec61f216bf198011eabf6f7 100644 (file)
@@ -488,6 +488,20 @@ static void report_tracking(struct branch_info *new)
        strbuf_release(&sb);
 }
 
+static void detach_advice(const char *old_path, const char *new_name)
+{
+       const char fmt[] =
+       "Note: checking out '%s'.\n\n"
+       "You are in 'detached HEAD' state. You can look around, make experimental\n"
+       "changes and commit them, and you can discard any commits you make in this\n"
+       "state without impacting any branches by performing another checkout.\n\n"
+       "If you want to create a new branch to retain commits you create, you may\n"
+       "do so (now or later) by using -b with the checkout command again. Example:\n\n"
+       "  git checkout -b new_branch_name\n\n";
+
+       fprintf(stderr, fmt, new_name);
+}
+
 static void update_refs_for_switch(struct checkout_opts *opts,
                                   struct branch_info *old,
                                   struct branch_info *new)
@@ -522,8 +536,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
                           REF_NODEREF, DIE_ON_ERR);
                if (!opts->quiet) {
-                       if (old->path)
-                               fprintf(stderr, "Note: moving to '%s' which isn't a local branch\nIf you want to create a new branch from this checkout, you may do so\n(now or later) by using -b with the checkout command again. Example:\n  git checkout -b <new_branch_name>\n", new->name);
+                       if (old->path && advice_detached_head)
+                               detach_advice(old->path, new->name);
                        describe_detached_head("HEAD is now at", new->commit);
                }
        }
index 55676fd874466c70445e81c69cf397cd01380aaf..f4c73442cfba9483a826dcfbf68f5466d43e8351 100644 (file)
@@ -41,7 +41,7 @@ static const char implicit_ident_advice[] =
 "on your username and hostname. Please check that they are accurate.\n"
 "You can suppress this message by setting them explicitly:\n"
 "\n"
-"    git config --global user.name Your Name\n"
+"    git config --global user.name \"Your Name\"\n"
 "    git config --global user.email you@example.com\n"
 "\n"
 "If the identity used for this commit is wrong, you can fix it with:\n"
@@ -1046,7 +1046,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        if (*argv)
                s.pathspec = get_pathspec(prefix, argv);
 
-       read_cache();
+       read_cache_preload(s.pathspec);
        refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
        s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
        s.in_merge = in_merge;
index 8ed4a6feaac2868523e6516e02865132dedbc9f5..dbd8b7bcc8b5ddea9bec3a0c774346007cb0d31c 100644 (file)
@@ -586,12 +586,12 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
        return retval;
 }
 
-static int sideband_demux(int fd, void *data)
+static int sideband_demux(int in, int out, void *data)
 {
        int *xd = data;
 
-       int ret = recv_sideband("fetch-pack", xd[0], fd);
-       close(fd);
+       int ret = recv_sideband("fetch-pack", xd[0], out);
+       close(out);
        return ret;
 }
 
@@ -613,6 +613,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
                 */
                demux.proc = sideband_demux;
                demux.data = xd;
+               demux.out = -1;
                if (start_async(&demux))
                        die("fetch-pack: unable to fork off sideband"
                            " demultiplexer");
index 26979577d3f753cdc8b3358954249708d4cb53ef..371db0aa9e6572c5b25ea679a510bc8784a25759 100644 (file)
@@ -844,6 +844,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        opt.relative = 1;
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
+       opt.header_tail = &opt.header_list;
        opt.regflags = REG_NEWLINE;
        opt.max_depth = -1;
 
index a50ac2256cdbacd76ed44a50804212be07f949db..ce2ef6bede40fde8823336bc85762bd3e7cd4760 100644 (file)
@@ -779,8 +779,7 @@ static int handle_commit_msg(struct strbuf *line)
                return 0;
 
        if (still_looking) {
-               strbuf_ltrim(line);
-               if (!line->len)
+               if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
                        return 0;
        }
 
index e1d3adf405bb6ac842a3415e0461b4772396060d..539e75d56f7a33fdb1971b51ee0681c43e9662b0 100644 (file)
@@ -464,9 +464,6 @@ static int write_one(struct sha1file *f,
        return 1;
 }
 
-/* forward declaration for write_pack_file */
-static int adjust_perm(const char *path, mode_t mode);
-
 static void write_pack_file(void)
 {
        uint32_t i = 0, j;
@@ -523,21 +520,17 @@ static void write_pack_file(void)
                }
 
                if (!pack_to_stdout) {
-                       mode_t mode = umask(0);
                        struct stat st;
                        const char *idx_tmp_name;
                        char tmpname[PATH_MAX];
 
-                       umask(mode);
-                       mode = 0444 & ~mode;
-
                        idx_tmp_name = write_idx_file(NULL, written_list,
                                                      nr_written, sha1);
 
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
                                 base_name, sha1_to_hex(sha1));
                        free_pack_by_name(tmpname);
-                       if (adjust_perm(pack_tmp_name, mode))
+                       if (adjust_shared_perm(pack_tmp_name))
                                die_errno("unable to make temporary pack file readable");
                        if (rename(pack_tmp_name, tmpname))
                                die_errno("unable to rename temporary pack file");
@@ -565,7 +558,7 @@ static void write_pack_file(void)
 
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
                                 base_name, sha1_to_hex(sha1));
-                       if (adjust_perm(idx_tmp_name, mode))
+                       if (adjust_shared_perm(idx_tmp_name))
                                die_errno("unable to make temporary index file readable");
                        if (rename(idx_tmp_name, tmpname))
                                die_errno("unable to rename temporary index file");
@@ -2125,13 +2118,6 @@ static void get_object_list(int ac, const char **av)
                loosen_unused_packed_objects(&revs);
 }
 
-static int adjust_perm(const char *path, mode_t mode)
-{
-       if (chmod(path, mode))
-               return -1;
-       return adjust_shared_perm(path);
-}
-
 int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 {
        int use_internal_rev_list = 0;
index 4320c93e700a08911e42e3e949656af67b675244..0559fcc871894548050e99c8c258561a0dcad5c9 100644 (file)
@@ -2,6 +2,7 @@
 #include "pack.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "sideband.h"
 #include "run-command.h"
 #include "exec_cmd.h"
 #include "commit.h"
@@ -27,11 +28,12 @@ static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static int use_sideband;
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static const char *head_name;
-static char *capabilities_to_send;
+static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -105,19 +107,21 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
 
 static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       if (!capabilities_to_send)
+       if (sent_capabilities)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
        else
-               packet_write(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, capabilities_to_send);
-       capabilities_to_send = NULL;
+               packet_write(1, "%s %s%c%s%s\n",
+                            sha1_to_hex(sha1), path, 0,
+                            " report-status delete-refs side-band-64k",
+                            prefer_ofs_delta ? " ofs-delta" : "");
+       sent_capabilities = 1;
        return 0;
 }
 
 static void write_head_info(void)
 {
        for_each_ref(show_ref, NULL);
-       if (capabilities_to_send)
+       if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
 
 }
@@ -135,11 +139,61 @@ static struct command *commands;
 static const char pre_receive_hook[] = "hooks/pre-receive";
 static const char post_receive_hook[] = "hooks/post-receive";
 
+static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+
+static void report_message(const char *prefix, const char *err, va_list params)
+{
+       int sz = strlen(prefix);
+       char msg[4096];
+
+       strncpy(msg, prefix, sz);
+       sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params);
+       if (sz > (sizeof(msg) - 1))
+               sz = sizeof(msg) - 1;
+       msg[sz++] = '\n';
+
+       if (use_sideband)
+               send_sideband(1, 2, msg, sz, use_sideband);
+       else
+               xwrite(2, msg, sz);
+}
+
+static void rp_warning(const char *err, ...)
+{
+       va_list params;
+       va_start(params, err);
+       report_message("warning: ", err, params);
+       va_end(params);
+}
+
+static void rp_error(const char *err, ...)
+{
+       va_list params;
+       va_start(params, err);
+       report_message("error: ", err, params);
+       va_end(params);
+}
+
+static int copy_to_sideband(int in, int out, void *arg)
+{
+       char data[128];
+       while (1) {
+               ssize_t sz = xread(in, data, sizeof(data));
+               if (sz <= 0)
+                       break;
+               send_sideband(1, 2, data, sz, use_sideband);
+       }
+       close(in);
+       return 0;
+}
+
 static int run_receive_hook(const char *hook_name)
 {
        static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
        struct command *cmd;
        struct child_process proc;
+       struct async muxer;
        const char *argv[2];
        int have_input = 0, code;
 
@@ -159,9 +213,23 @@ static int run_receive_hook(const char *hook_name)
        proc.in = -1;
        proc.stdout_to_stderr = 1;
 
+       if (use_sideband) {
+               memset(&muxer, 0, sizeof(muxer));
+               muxer.proc = copy_to_sideband;
+               muxer.in = -1;
+               code = start_async(&muxer);
+               if (code)
+                       return code;
+               proc.err = muxer.in;
+       }
+
        code = start_command(&proc);
-       if (code)
+       if (code) {
+               if (use_sideband)
+                       finish_async(&muxer);
                return code;
+       }
+
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string) {
                        size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -173,6 +241,8 @@ static int run_receive_hook(const char *hook_name)
                }
        }
        close(proc.in);
+       if (use_sideband)
+               finish_async(&muxer);
        return finish_command(&proc);
 }
 
@@ -180,6 +250,8 @@ static int run_update_hook(struct command *cmd)
 {
        static const char update_hook[] = "hooks/update";
        const char *argv[5];
+       struct child_process proc;
+       int code;
 
        if (access(update_hook, X_OK) < 0)
                return 0;
@@ -190,8 +262,18 @@ static int run_update_hook(struct command *cmd)
        argv[3] = sha1_to_hex(cmd->new_sha1);
        argv[4] = NULL;
 
-       return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
-                                       RUN_COMMAND_STDOUT_TO_STDERR);
+       memset(&proc, 0, sizeof(proc));
+       proc.no_stdin = 1;
+       proc.stdout_to_stderr = 1;
+       proc.err = use_sideband ? -1 : 0;
+       proc.argv = argv;
+
+       code = start_command(&proc);
+       if (code)
+               return code;
+       if (use_sideband)
+               copy_to_sideband(proc.err, -1, NULL);
+       return finish_command(&proc);
 }
 
 static int is_ref_checked_out(const char *ref)
@@ -224,7 +306,7 @@ static void refuse_unconfigured_deny(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(refuse_unconfigured_deny_msg); i++)
-               error("%s", refuse_unconfigured_deny_msg[i]);
+               rp_error("%s", refuse_unconfigured_deny_msg[i]);
 }
 
 static char *refuse_unconfigured_deny_delete_current_msg[] = {
@@ -244,7 +326,7 @@ static void refuse_unconfigured_deny_delete_current(void)
        for (i = 0;
             i < ARRAY_SIZE(refuse_unconfigured_deny_delete_current_msg);
             i++)
-               error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
+               rp_error("%s", refuse_unconfigured_deny_delete_current_msg[i]);
 }
 
 static const char *update(struct command *cmd)
@@ -256,7 +338,7 @@ static const char *update(struct command *cmd)
 
        /* only refs/... are allowed */
        if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
-               error("refusing to create funny ref '%s' remotely", name);
+               rp_error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
 
@@ -265,11 +347,11 @@ static const char *update(struct command *cmd)
                case DENY_IGNORE:
                        break;
                case DENY_WARN:
-                       warning("updating the current branch");
+                       rp_warning("updating the current branch");
                        break;
                case DENY_REFUSE:
                case DENY_UNCONFIGURED:
-                       error("refusing to update checked out branch: %s", name);
+                       rp_error("refusing to update checked out branch: %s", name);
                        if (deny_current_branch == DENY_UNCONFIGURED)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
@@ -284,7 +366,7 @@ static const char *update(struct command *cmd)
 
        if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
                if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
-                       error("denying ref deletion for %s", name);
+                       rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
                }
 
@@ -293,13 +375,13 @@ static const char *update(struct command *cmd)
                        case DENY_IGNORE:
                                break;
                        case DENY_WARN:
-                               warning("deleting the current branch");
+                               rp_warning("deleting the current branch");
                                break;
                        case DENY_REFUSE:
                        case DENY_UNCONFIGURED:
                                if (deny_delete_current == DENY_UNCONFIGURED)
                                        refuse_unconfigured_deny_delete_current();
-                               error("refusing to delete the current branch: %s", name);
+                               rp_error("refusing to delete the current branch: %s", name);
                                return "deletion of the current branch prohibited";
                        }
                }
@@ -329,23 +411,23 @@ static const char *update(struct command *cmd)
                                break;
                free_commit_list(bases);
                if (!ent) {
-                       error("denying non-fast-forward %s"
-                             " (you should pull first)", name);
+                       rp_error("denying non-fast-forward %s"
+                                " (you should pull first)", name);
                        return "non-fast-forward";
                }
        }
        if (run_update_hook(cmd)) {
-               error("hook declined to update %s", name);
+               rp_error("hook declined to update %s", name);
                return "hook declined";
        }
 
        if (is_null_sha1(new_sha1)) {
                if (!parse_object(old_sha1)) {
-                       warning ("Allowing deletion of corrupt ref.");
+                       rp_warning("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
                if (delete_ref(name, old_sha1, 0)) {
-                       error("failed to delete %s", name);
+                       rp_error("failed to delete %s", name);
                        return "failed to delete";
                }
                return NULL; /* good */
@@ -353,7 +435,7 @@ static const char *update(struct command *cmd)
        else {
                lock = lock_any_ref_for_update(name, old_sha1, 0);
                if (!lock) {
-                       error("failed to lock %s", name);
+                       rp_error("failed to lock %s", name);
                        return "failed to lock";
                }
                if (write_ref_sha1(lock, new_sha1, "push")) {
@@ -368,8 +450,9 @@ static char update_post_hook[] = "hooks/post-update";
 static void run_update_post_hook(struct command *cmd)
 {
        struct command *cmd_p;
-       int argc, status;
+       int argc;
        const char **argv;
+       struct child_process proc;
 
        for (argc = 0, cmd_p = cmd; cmd_p; cmd_p = cmd_p->next) {
                if (cmd_p->error_string)
@@ -391,8 +474,18 @@ static void run_update_post_hook(struct command *cmd)
                argc++;
        }
        argv[argc] = NULL;
-       status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
-                       | RUN_COMMAND_STDOUT_TO_STDERR);
+
+       memset(&proc, 0, sizeof(proc));
+       proc.no_stdin = 1;
+       proc.stdout_to_stderr = 1;
+       proc.err = use_sideband ? -1 : 0;
+       proc.argv = argv;
+
+       if (!start_command(&proc)) {
+               if (use_sideband)
+                       copy_to_sideband(proc.err, -1, NULL);
+               finish_command(&proc);
+       }
 }
 
 static void execute_commands(const char *unpacker_error)
@@ -452,6 +545,8 @@ static void read_head_info(void)
                if (reflen + 82 < len) {
                        if (strstr(refname + reflen + 1, "report-status"))
                                report_status = 1;
+                       if (strstr(refname + reflen + 1, "side-band-64k"))
+                               use_sideband = LARGE_PACKET_MAX;
                }
                cmd = xmalloc(sizeof(struct command) + len - 80);
                hashcpy(cmd->old_sha1, old_sha1);
@@ -551,17 +646,25 @@ static const char *unpack(void)
 static void report(const char *unpack_status)
 {
        struct command *cmd;
-       packet_write(1, "unpack %s\n",
-                    unpack_status ? unpack_status : "ok");
+       struct strbuf buf = STRBUF_INIT;
+
+       packet_buf_write(&buf, "unpack %s\n",
+                        unpack_status ? unpack_status : "ok");
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string)
-                       packet_write(1, "ok %s\n",
-                                    cmd->ref_name);
+                       packet_buf_write(&buf, "ok %s\n",
+                                        cmd->ref_name);
                else
-                       packet_write(1, "ng %s %s\n",
-                                    cmd->ref_name, cmd->error_string);
+                       packet_buf_write(&buf, "ng %s %s\n",
+                                        cmd->ref_name, cmd->error_string);
        }
-       packet_flush(1);
+       packet_buf_flush(&buf);
+
+       if (use_sideband)
+               send_sideband(1, 1, buf.buf, buf.len, use_sideband);
+       else
+               safe_write(1, buf.buf, buf.len);
+       strbuf_release(&buf);
 }
 
 static int delete_only(struct command *cmd)
@@ -658,10 +761,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
 
-       capabilities_to_send = (prefer_ofs_delta) ?
-               " report-status delete-refs ofs-delta " :
-               " report-status delete-refs ";
-
        if (advertise_refs || !stateless_rpc) {
                add_alternate_refs();
                write_head_info();
@@ -695,5 +794,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                if (auto_update_server_info)
                        update_server_info(0);
        }
+       if (use_sideband)
+               packet_flush(1);
        return 0;
 }
index c924b3a2c76c1f9a7f5531504825ed1b5456d41a..5679170e82ed644d4c3eb4f71f26aa0ac9acce24 100644 (file)
@@ -371,8 +371,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
            revs.diff)
                usage(rev_list_usage);
 
-       save_commit_buffer = revs.verbose_header ||
-               revs.grep_filter.pattern_list;
+       save_commit_buffer = (revs.verbose_header ||
+                             revs.grep_filter.pattern_list ||
+                             revs.grep_filter.header_list);
        if (bisect_list)
                revs.limited = 1;
 
index 76c72065de73ea3f0da4665c0a47a64610e2ead2..2183a470524048eabef1b0f31499c5d04aec5850 100644 (file)
@@ -372,6 +372,14 @@ static void print_helper_status(struct ref *ref)
        strbuf_release(&buf);
 }
 
+static int sideband_demux(int in, int out, void *data)
+{
+       int *fd = data;
+       int ret = recv_sideband("send-pack", fd[0], out);
+       close(out);
+       return ret;
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
@@ -382,18 +390,22 @@ int send_pack(struct send_pack_args *args,
        struct strbuf req_buf = STRBUF_INIT;
        struct ref *ref;
        int new_refs;
-       int ask_for_status_report = 0;
        int allow_deleting_refs = 0;
-       int expect_status_report = 0;
+       int status_report = 0;
+       int use_sideband = 0;
+       unsigned cmds_sent = 0;
        int ret;
+       struct async demux;
 
        /* Does the other end support the reporting? */
        if (server_supports("report-status"))
-               ask_for_status_report = 1;
+               status_report = 1;
        if (server_supports("delete-refs"))
                allow_deleting_refs = 1;
        if (server_supports("ofs-delta"))
                args->use_ofs_delta = 1;
+       if (server_supports("side-band-64k"))
+               use_sideband = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -426,28 +438,30 @@ int send_pack(struct send_pack_args *args,
                if (!ref->deletion)
                        new_refs++;
 
-               if (!args->dry_run) {
+               if (args->dry_run) {
+                       ref->status = REF_STATUS_OK;
+               } else {
                        char *old_hex = sha1_to_hex(ref->old_sha1);
                        char *new_hex = sha1_to_hex(ref->new_sha1);
 
-                       if (ask_for_status_report) {
-                               packet_buf_write(&req_buf, "%s %s %s%c%s",
+                       if (!cmds_sent && (status_report || use_sideband)) {
+                               packet_buf_write(&req_buf, "%s %s %s%c%s%s",
                                        old_hex, new_hex, ref->name, 0,
-                                       "report-status");
-                               ask_for_status_report = 0;
-                               expect_status_report = 1;
+                                       status_report ? " report-status" : "",
+                                       use_sideband ? " side-band-64k" : "");
                        }
                        else
                                packet_buf_write(&req_buf, "%s %s %s",
                                        old_hex, new_hex, ref->name);
+                       ref->status = status_report ?
+                               REF_STATUS_EXPECTING_REPORT :
+                               REF_STATUS_OK;
+                       cmds_sent++;
                }
-               ref->status = expect_status_report ?
-                       REF_STATUS_EXPECTING_REPORT :
-                       REF_STATUS_OK;
        }
 
        if (args->stateless_rpc) {
-               if (!args->dry_run) {
+               if (!args->dry_run && cmds_sent) {
                        packet_buf_flush(&req_buf);
                        send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
                }
@@ -457,23 +471,43 @@ int send_pack(struct send_pack_args *args,
        }
        strbuf_release(&req_buf);
 
-       if (new_refs && !args->dry_run) {
+       if (use_sideband && cmds_sent) {
+               memset(&demux, 0, sizeof(demux));
+               demux.proc = sideband_demux;
+               demux.data = fd;
+               demux.out = -1;
+               if (start_async(&demux))
+                       die("receive-pack: unable to fork off sideband demultiplexer");
+               in = demux.out;
+       }
+
+       if (new_refs && cmds_sent) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
                                ref->status = REF_STATUS_NONE;
+                       if (use_sideband)
+                               finish_async(&demux);
                        return -1;
                }
        }
-       if (args->stateless_rpc && !args->dry_run)
+       if (args->stateless_rpc && cmds_sent)
                packet_flush(out);
 
-       if (expect_status_report)
+       if (status_report && cmds_sent)
                ret = receive_status(in, remote_refs);
        else
                ret = 0;
        if (args->stateless_rpc)
                packet_flush(out);
 
+       if (use_sideband && cmds_sent) {
+               if (finish_async(&demux)) {
+                       error("error in sideband demultiplexer");
+                       ret = -1;
+               }
+               close(demux.out);
+       }
+
        if (ret < 0)
                return ret;
        for (ref = remote_refs; ref; ref = ref->next) {
index b3b055f68ce59b6b91ef6949bd8c4bd0bed68b55..ecd2d45a00b1aacf9d3a19e2f934a14ca7e58c35 100644 (file)
@@ -304,9 +304,19 @@ parse_done:
        return 0;
 }
 
+static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
+                                    const struct shortlog *log)
+{
+       int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
+       if (col != log->wrap)
+               strbuf_addch(sb, '\n');
+}
+
 void shortlog_output(struct shortlog *log)
 {
        int i, j;
+       struct strbuf sb = STRBUF_INIT;
+
        if (log->sort_by_number)
                qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
                        compare_by_number);
@@ -321,9 +331,9 @@ void shortlog_output(struct shortlog *log)
                                const char *msg = onelines->items[j].string;
 
                                if (log->wrap_lines) {
-                                       int col = print_wrapped_text(msg, log->in1, log->in2, log->wrap);
-                                       if (col != log->wrap)
-                                               putchar('\n');
+                                       strbuf_reset(&sb);
+                                       add_wrapped_shortlog_msg(&sb, msg, log);
+                                       fwrite(sb.buf, sb.len, 1, stdout);
                                }
                                else
                                        printf("      %s\n", msg);
@@ -337,6 +347,7 @@ void shortlog_output(struct shortlog *log)
                log->list.items[i].util = NULL;
        }
 
+       strbuf_release(&sb);
        log->list.strdup_strings = 1;
        string_list_clear(&log->list, 1);
        clear_mailmap(&log->mailmap);
index 22805181900c3cd33da0b92a2560b19a0cf68842..70fdb4dec7e8b0c56ded90ee7f086cc9f16293a1 100644 (file)
@@ -6,7 +6,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 
-static const char var_usage[] = "git var [-l | <variable>]";
+static const char var_usage[] = "git var (-l | <variable>)";
 
 static const char *editor(int flag)
 {
@@ -20,7 +20,7 @@ static const char *editor(int flag)
 
 static const char *pager(int flag)
 {
-       const char *pgm = git_pager();
+       const char *pgm = git_pager(1);
 
        if (!pgm)
                pgm = "cat";
diff --git a/cache.h b/cache.h
index d478eff1f323f25a474cf019e0de2254c5ff0360..4d89aa3da4d32653289c742f37abe8ef604ab11b 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -641,6 +641,10 @@ int git_mkstemp(char *path, size_t n, const char *template);
 
 int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 
+/* set default permissions by passing mode arguments to open(2) */
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
+int git_mkstemp_mode(char *pattern, int mode);
+
 /*
  * NOTE NOTE NOTE!!
  *
@@ -775,7 +779,7 @@ extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 extern const char *fmt_name(const char *name, const char *email);
 extern const char *git_editor(void);
-extern const char *git_pager(void);
+extern const char *git_pager(int stdout_is_tty);
 
 struct checkout {
        const char *base_dir;
diff --git a/compat/mkstemps.c b/compat/mkstemps.c
deleted file mode 100644 (file)
index 14179c8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "../git-compat-util.h"
-
-/* Adapted from libiberty's mkstemp.c. */
-
-#undef TMP_MAX
-#define TMP_MAX 16384
-
-int gitmkstemps(char *pattern, int suffix_len)
-{
-       static const char letters[] =
-               "abcdefghijklmnopqrstuvwxyz"
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-               "0123456789";
-       static const int num_letters = 62;
-       uint64_t value;
-       struct timeval tv;
-       char *template;
-       size_t len;
-       int fd, count;
-
-       len = strlen(pattern);
-
-       if (len < 6 + suffix_len) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       /*
-        * Replace pattern's XXXXXX characters with randomness.
-        * Try TMP_MAX different filenames.
-        */
-       gettimeofday(&tv, NULL);
-       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
-       template = &pattern[len - 6 - suffix_len];
-       for (count = 0; count < TMP_MAX; ++count) {
-               uint64_t v = value;
-               /* Fill in the random bits. */
-               template[0] = letters[v % num_letters]; v /= num_letters;
-               template[1] = letters[v % num_letters]; v /= num_letters;
-               template[2] = letters[v % num_letters]; v /= num_letters;
-               template[3] = letters[v % num_letters]; v /= num_letters;
-               template[4] = letters[v % num_letters]; v /= num_letters;
-               template[5] = letters[v % num_letters]; v /= num_letters;
-
-               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600);
-               if (fd > 0)
-                       return fd;
-               /*
-                * Fatal error (EPERM, ENOSPC etc).
-                * It doesn't make sense to loop.
-                */
-               if (errno != EEXIST)
-                       break;
-               /*
-                * This is a random value.  It is only necessary that
-                * the next TMP_MAX values generated by adding 7777 to
-                * VALUE are different with (module 2^32).
-                */
-               value += 7777;
-       }
-       /* We return the null string if we can't find a unique file name.  */
-       pattern[0] = '\0';
-       errno = EINVAL;
-       return -1;
-}
index 27acce58bc4bec60a394f03db1f6e60e1e4cfc3e..4f8fcb7bbb00b66f1eaa354119784e6ef57e1eb4 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -241,7 +241,7 @@ struct filter_params {
        const char *cmd;
 };
 
-static int filter_buffer(int fd, void *data)
+static int filter_buffer(int in, int out, void *data)
 {
        /*
         * Spawn cmd and feed the buffer contents through its stdin.
@@ -255,7 +255,7 @@ static int filter_buffer(int fd, void *data)
        child_process.argv = argv;
        child_process.use_shell = 1;
        child_process.in = -1;
-       child_process.out = fd;
+       child_process.out = out;
 
        if (start_command(&child_process))
                return error("cannot fork to run external filter %s", params->cmd);
@@ -292,6 +292,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
        memset(&async, 0, sizeof(async));
        async.proc = filter_buffer;
        async.data = &params;
+       async.out = -1;
        params.src = src;
        params.size = len;
        params.cmd = cmd;
diff --git a/diff.c b/diff.c
index 989dbc54cbb31c095cc45fb5bb74f62077f1c635..0d465faa1e546382267dc0779116a013647ecf41 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3522,6 +3522,29 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
+       if (output_format & DIFF_FORMAT_NO_OUTPUT &&
+           DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
+           DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+               /*
+                * run diff_flush_patch for the exit status. setting
+                * options->file to /dev/null should be safe, becaue we
+                * aren't supposed to produce any output anyway.
+                */
+               if (options->close_file)
+                       fclose(options->file);
+               options->file = fopen("/dev/null", "w");
+               if (!options->file)
+                       die_errno("Could not open /dev/null");
+               options->close_file = 1;
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (check_pair_status(p))
+                               diff_flush_patch(p, options);
+                       if (options->found_changes)
+                               break;
+               }
+       }
+
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
                        putc(options->line_termination, options->file);
index b477dc6a8f8104cecf21b68375b7c5cf7ab8842c..74f08bd554ae4cab1dc9ee38272376e5cbec1200 100644 (file)
@@ -164,12 +164,11 @@ Format of STDIN stream:
 
 struct object_entry
 {
+       struct pack_idx_entry idx;
        struct object_entry *next;
-       uint32_t offset;
        uint32_t type : TYPE_BITS,
                pack_id : PACK_ID_BITS,
                depth : DEPTH_BITS;
-       unsigned char sha1[20];
 };
 
 struct object_entry_pool
@@ -192,7 +191,7 @@ struct mark_set
 struct last_object
 {
        struct strbuf data;
-       uint32_t offset;
+       off_t offset;
        unsigned int depth;
        unsigned no_swap : 1;
 };
@@ -280,7 +279,7 @@ struct recent_command
 
 /* Configured limits on output */
 static unsigned long max_depth = 10;
-static off_t max_packsize = (1LL << 32) - 1;
+static off_t max_packsize;
 static uintmax_t big_file_threshold = 512 * 1024 * 1024;
 static int force_update;
 static int pack_compression_level = Z_DEFAULT_COMPRESSION;
@@ -313,9 +312,10 @@ static struct atom_str **atom_table;
 
 /* The .pack file being generated */
 static unsigned int pack_id;
+static struct sha1file *pack_file;
 static struct packed_git *pack_data;
 static struct packed_git **all_packs;
-static unsigned long pack_size;
+static off_t pack_size;
 
 /* Table of objects we've written. */
 static unsigned int object_entry_alloc = 5000;
@@ -521,7 +521,7 @@ static struct object_entry *new_object(unsigned char *sha1)
                alloc_objects(object_entry_alloc);
 
        e = blocks->next_free++;
-       hashcpy(e->sha1, sha1);
+       hashcpy(e->idx.sha1, sha1);
        return e;
 }
 
@@ -530,7 +530,7 @@ static struct object_entry *find_object(unsigned char *sha1)
        unsigned int h = sha1[0] << 8 | sha1[1];
        struct object_entry *e;
        for (e = object_table[h]; e; e = e->next)
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
        return NULL;
 }
@@ -542,7 +542,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
        struct object_entry *p = NULL;
 
        while (e) {
-               if (!hashcmp(sha1, e->sha1))
+               if (!hashcmp(sha1, e->idx.sha1))
                        return e;
                p = e;
                e = e->next;
@@ -550,7 +550,7 @@ static struct object_entry *insert_object(unsigned char *sha1)
 
        e = new_object(sha1);
        e->next = NULL;
-       e->offset = 0;
+       e->idx.offset = 0;
        if (p)
                p->next = e;
        else
@@ -839,11 +839,12 @@ static void start_packfile(void)
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
        p->pack_fd = pack_fd;
+       pack_file = sha1fd(pack_fd, p->pack_name);
 
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
        hdr.hdr_version = htonl(2);
        hdr.hdr_entries = 0;
-       write_or_die(p->pack_fd, &hdr, sizeof(hdr));
+       sha1write(pack_file, &hdr, sizeof(hdr));
 
        pack_data = p;
        pack_size = sizeof(hdr);
@@ -853,67 +854,30 @@ static void start_packfile(void)
        all_packs[pack_id] = p;
 }
 
-static int oecmp (const void *a_, const void *b_)
-{
-       struct object_entry *a = *((struct object_entry**)a_);
-       struct object_entry *b = *((struct object_entry**)b_);
-       return hashcmp(a->sha1, b->sha1);
-}
-
-static char *create_index(void)
+static const char *create_index(void)
 {
-       static char tmpfile[PATH_MAX];
-       git_SHA_CTX ctx;
-       struct sha1file *f;
-       struct object_entry **idx, **c, **last, *e;
+       const char *tmpfile;
+       struct pack_idx_entry **idx, **c, **last;
+       struct object_entry *e;
        struct object_entry_pool *o;
-       uint32_t array[256];
-       int i, idx_fd;
 
-       /* Build the sorted table of object IDs. */
-       idx = xmalloc(object_count * sizeof(struct object_entry*));
+       /* Build the table of object IDs. */
+       idx = xmalloc(object_count * sizeof(*idx));
        c = idx;
        for (o = blocks; o; o = o->next_pool)
                for (e = o->next_free; e-- != o->entries;)
                        if (pack_id == e->pack_id)
-                               *c++ = e;
+                               *c++ = &e->idx;
        last = idx + object_count;
        if (c != last)
                die("internal consistency error creating the index");
-       qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
 
-       /* Generate the fan-out array. */
-       c = idx;
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = c;
-               while (next < last) {
-                       if ((*next)->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - idx);
-               c = next;
-       }
-
-       idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
-                            "pack/tmp_idx_XXXXXX");
-       f = sha1fd(idx_fd, tmpfile);
-       sha1write(f, array, 256 * sizeof(int));
-       git_SHA1_Init(&ctx);
-       for (c = idx; c != last; c++) {
-               uint32_t offset = htonl((*c)->offset);
-               sha1write(f, &offset, 4);
-               sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
-               git_SHA1_Update(&ctx, (*c)->sha1, 20);
-       }
-       sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
-       sha1close(f, NULL, CSUM_FSYNC);
+       tmpfile = write_idx_file(NULL, idx, object_count, pack_data->sha1);
        free(idx);
-       git_SHA1_Final(pack_data->sha1, &ctx);
        return tmpfile;
 }
 
-static char *keep_pack(char *curr_index_name)
+static char *keep_pack(const char *curr_index_name)
 {
        static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
@@ -935,6 +899,7 @@ static char *keep_pack(char *curr_index_name)
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
        if (move_temp_to_file(curr_index_name, name))
                die("cannot store index file");
+       free((void *)curr_index_name);
        return name;
 }
 
@@ -957,15 +922,17 @@ static void end_packfile(void)
 
        clear_delta_base_cache();
        if (object_count) {
+               unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                struct branch *b;
                struct tag *t;
 
                close_pack_windows(pack_data);
+               sha1close(pack_file, cur_pack_sha1, 0);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
-                                   NULL, 0);
+                                   cur_pack_sha1, pack_size);
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
@@ -1063,25 +1030,21 @@ static int store_object(
        e = insert_object(sha1);
        if (mark)
                insert_mark(mark, e);
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = type;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[type]++;
                return 1;
        }
 
-       if (last && last->data.buf && last->depth < max_depth) {
+       if (last && last->data.buf && last->depth < max_depth && dat->len > 20) {
                delta = diff_delta(last->data.buf, last->data.len,
                        dat->buf, dat->len,
-                       &deltalen, 0);
-               if (delta && deltalen >= dat->len) {
-                       free(delta);
-                       delta = NULL;
-               }
+                       &deltalen, dat->len - 20);
        } else
                delta = NULL;
 
@@ -1101,7 +1064,7 @@ static int store_object(
        deflateEnd(&s);
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + s.total_out) > max_packsize
+       if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
                || (pack_size + 60 + s.total_out) < pack_size) {
 
                /* This new object needs to *not* have the current pack_id. */
@@ -1127,36 +1090,40 @@ static int store_object(
 
        e->type = type;
        e->pack_id = pack_id;
-       e->offset = pack_size;
+       e->idx.offset = pack_size;
        object_count++;
        object_count_by_type[type]++;
 
+       crc32_begin(pack_file);
+
        if (delta) {
-               unsigned long ofs = e->offset - last->offset;
+               off_t ofs = e->idx.offset - last->offset;
                unsigned pos = sizeof(hdr) - 1;
 
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
 
                hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
 
                hdr[pos] = ofs & 127;
                while (ofs >>= 7)
                        hdr[--pos] = 128 | (--ofs & 127);
-               write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
+               sha1write(pack_file, hdr + pos, sizeof(hdr) - pos);
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
                hdrlen = encode_header(type, dat->len, hdr);
-               write_or_die(pack_data->pack_fd, hdr, hdrlen);
+               sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
 
-       write_or_die(pack_data->pack_fd, out, s.total_out);
+       sha1write(pack_file, out, s.total_out);
        pack_size += s.total_out;
 
+       e->idx.crc32 = crc32_end(pack_file);
+
        free(out);
        free(delta);
        if (last) {
@@ -1165,18 +1132,23 @@ static int store_object(
                } else {
                        strbuf_swap(&last->data, dat);
                }
-               last->offset = e->offset;
+               last->offset = e->idx.offset;
                last->depth = e->depth;
        }
        return 0;
 }
 
-static void truncate_pack(off_t to)
+static void truncate_pack(off_t to, git_SHA_CTX *ctx)
 {
        if (ftruncate(pack_data->pack_fd, to)
         || lseek(pack_data->pack_fd, to, SEEK_SET) != to)
                die_errno("cannot truncate pack to skip duplicate");
        pack_size = to;
+
+       /* yes this is a layering violation */
+       pack_file->total = to;
+       pack_file->offset = 0;
+       pack_file->ctx = *ctx;
 }
 
 static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
@@ -1189,16 +1161,21 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        unsigned long hdrlen;
        off_t offset;
        git_SHA_CTX c;
+       git_SHA_CTX pack_file_ctx;
        z_stream s;
        int status = Z_OK;
 
        /* Determine if we should auto-checkpoint. */
-       if ((pack_size + 60 + len) > max_packsize
+       if ((max_packsize && (pack_size + 60 + len) > max_packsize)
                || (pack_size + 60 + len) < pack_size)
                cycle_packfile();
 
        offset = pack_size;
 
+       /* preserve the pack_file SHA1 ctx in case we have to truncate later */
+       sha1flush(pack_file);
+       pack_file_ctx = pack_file->ctx;
+
        hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
        if (out_sz <= hdrlen)
                die("impossibly large object header");
@@ -1206,6 +1183,8 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, out_buf, hdrlen);
 
+       crc32_begin(pack_file);
+
        memset(&s, 0, sizeof(s));
        deflateInit(&s, pack_compression_level);
 
@@ -1233,7 +1212,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
 
                if (!s.avail_out || status == Z_STREAM_END) {
                        size_t n = s.next_out - out_buf;
-                       write_or_die(pack_data->pack_fd, out_buf, n);
+                       sha1write(pack_file, out_buf, n);
                        pack_size += n;
                        s.next_out = out_buf;
                        s.avail_out = out_sz;
@@ -1259,22 +1238,23 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        if (mark)
                insert_mark(mark, e);
 
-       if (e->offset) {
+       if (e->idx.offset) {
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else if (find_sha1_pack(sha1, packed_git)) {
                e->type = OBJ_BLOB;
                e->pack_id = MAX_PACK_ID;
-               e->offset = 1; /* just not zero! */
+               e->idx.offset = 1; /* just not zero! */
                duplicate_count_by_type[OBJ_BLOB]++;
-               truncate_pack(offset);
+               truncate_pack(offset, &pack_file_ctx);
 
        } else {
                e->depth = 0;
                e->type = OBJ_BLOB;
                e->pack_id = pack_id;
-               e->offset = offset;
+               e->idx.offset = offset;
+               e->idx.crc32 = crc32_end(pack_file);
                object_count++;
                object_count_by_type[OBJ_BLOB]++;
        }
@@ -1317,6 +1297,7 @@ static void *gfi_unpack_entry(
                 * the newly written data.
                 */
                close_pack_windows(p);
+               sha1flush(pack_file);
 
                /* We have to offer 20 bytes additional on the end of
                 * the packfile as the core unpacker code assumes the
@@ -1326,7 +1307,7 @@ static void *gfi_unpack_entry(
                 */
                p->pack_size = pack_size + 20;
        }
-       return unpack_entry(p, oe->offset, &type, sizep);
+       return unpack_entry(p, oe->idx.offset, &type, sizep);
 }
 
 static const char *get_mode(const char *str, uint16_t *modep)
@@ -1457,7 +1438,7 @@ static void store_tree(struct tree_entry *root)
        if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
                mktree(t, 0, &old_tree);
                lo.data = old_tree;
-               lo.offset = le->offset;
+               lo.offset = le->idx.offset;
                lo.depth = t->delta_depth;
        }
 
@@ -1715,7 +1696,7 @@ static void dump_marks_helper(FILE *f,
                for (k = 0; k < 1024; k++) {
                        if (m->data.marked[k])
                                fprintf(f, ":%" PRIuMAX " %s\n", base + k,
-                                       sha1_to_hex(m->data.marked[k]->sha1));
+                                       sha1_to_hex(m->data.marked[k]->idx.sha1));
                }
        }
 }
@@ -1798,7 +1779,7 @@ static void read_marks(void)
                        e = insert_object(sha1);
                        e->type = type;
                        e->pack_id = MAX_PACK_ID;
-                       e->offset = 1; /* just not zero! */
+                       e->idx.offset = 1; /* just not zero! */
                }
                insert_mark(mark, e);
        }
@@ -2183,7 +2164,7 @@ static void file_change_m(struct branch *b)
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2316,7 +2297,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
        if (*p == ':') {
                char *x;
                oe = find_mark(strtoumax(p + 1, &x, 10));
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
                p = x;
        } else if (!prefixcmp(p, "inline")) {
                inline_data = 1;
@@ -2339,7 +2320,7 @@ static void note_change_n(struct branch *b, unsigned char old_fanout)
                struct object_entry *commit_oe = find_mark(commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
-               hashcpy(commit_sha1, commit_oe->sha1);
+               hashcpy(commit_sha1, commit_oe->idx.sha1);
        } else if (!get_sha1(p, commit_sha1)) {
                unsigned long size;
                char *buf = read_object_with_reference(commit_sha1,
@@ -2446,7 +2427,7 @@ static int parse_from(struct branch *b)
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
-               hashcpy(b->sha1, oe->sha1);
+               hashcpy(b->sha1, oe->idx.sha1);
                if (oe->pack_id != MAX_PACK_ID) {
                        unsigned long size;
                        char *buf = gfi_unpack_entry(oe, &size);
@@ -2481,7 +2462,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                        struct object_entry *oe = find_mark(idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
-                       hashcpy(n->sha1, oe->sha1);
+                       hashcpy(n->sha1, oe->idx.sha1);
                } else if (!get_sha1(from, n->sha1)) {
                        unsigned long size;
                        char *buf = read_object_with_reference(n->sha1,
@@ -2639,7 +2620,7 @@ static void parse_new_tag(void)
                from_mark = strtoumax(from + 1, NULL, 10);
                oe = find_mark(from_mark);
                type = oe->type;
-               hashcpy(sha1, oe->sha1);
+               hashcpy(sha1, oe->idx.sha1);
        } else if (!get_sha1(from, sha1)) {
                unsigned long size;
                char *buf;
@@ -2891,6 +2872,17 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                pack_compression_seen = 1;
                return 0;
        }
+       if (!strcmp(k, "pack.indexversion")) {
+               pack_idx_default_version = git_config_int(k, v);
+               if (pack_idx_default_version > 2)
+                       die("bad pack.indexversion=%"PRIu32,
+                           pack_idx_default_version);
+               return 0;
+       }
+       if (!strcmp(k, "pack.packsizelimit")) {
+               max_packsize = git_config_ulong(k, v);
+               return 0;
+       }
        if (!strcmp(k, "core.bigfilethreshold")) {
                long n = git_config_int(k, v);
                big_file_threshold = 0 < n ? n : 0;
index cd43c3491260cb2aa51f0d19fd18ab66e4ad8217..21f1330a5bf26c955051ce9cf263289d90c7667b 100755 (executable)
@@ -957,6 +957,28 @@ sub coalesce_overlapping_hunks {
        return @out;
 }
 
+sub reassemble_patch {
+       my $head = shift;
+       my @patch;
+
+       # Include everything in the header except the beginning of the diff.
+       push @patch, (grep { !/^[-+]{3}/ } @$head);
+
+       # Then include any headers from the hunk lines, which must
+       # come before any actual hunk.
+       while (@_ && $_[0] !~ /^@/) {
+               push @patch, shift;
+       }
+
+       # Then begin the diff.
+       push @patch, grep { /^[-+]{3}/ } @$head;
+
+       # And then the actual hunks.
+       push @patch, @_;
+
+       return @patch;
+}
+
 sub color_diff {
        return map {
                colored((/^@/  ? $fraginfo_color :
@@ -1453,7 +1475,7 @@ sub patch_update_file {
 
        if (@result) {
                my $fh;
-               my @patch = (@{$head->{TEXT}}, @result);
+               my @patch = reassemble_patch($head->{TEXT}, @result);
                my $apply_routine = $patch_mode_flavour{APPLY};
                &$apply_routine(@patch);
                refresh();
index 3c08d53161faa72744ab64a1391d85cf9243f006..9df951a597d6b9ec5f7f390c35a60adb942a3fdc 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -663,10 +663,7 @@ do
                [eE]*) git_editor "$dotest/final-commit"
                       action=again ;;
                [vV]*) action=again
-                      : ${GIT_PAGER=$(git var GIT_PAGER)}
-                      : ${LESS=-FRSX}
-                      export LESS
-                      $GIT_PAGER "$dotest/patch" ;;
+                      git_pager "$dotest/patch" ;;
                *)     action=again ;;
                esac
            done
@@ -776,6 +773,5 @@ do
        go_next
 done
 
-git gc --auto
-
 rm -fr "$dotest"
+git gc --auto
index d56426dd396190a07de7d661cd22fb284b759ca8..7a095665d71f153a970243a9d03d919ddef29c88 100755 (executable)
@@ -107,6 +107,19 @@ git_editor() {
        eval "$GIT_EDITOR" '"$@"'
 }
 
+git_pager() {
+       if test -t 1
+       then
+               GIT_PAGER=$(git var GIT_PAGER)
+       else
+               GIT_PAGER=cat
+       fi
+       : ${LESS=-FRSX}
+       export LESS
+
+       eval "$GIT_PAGER" '"$@"'
+}
+
 sane_grep () {
        GREP_OPTIONS= LC_ALL=C grep "$@"
 }
@@ -128,7 +141,7 @@ cd_to_toplevel () {
 }
 
 require_work_tree () {
-       test $(git rev-parse --is-inside-work-tree) = true ||
+       test "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = true ||
        die "fatal: $0 cannot be used without a working tree."
 }
 
index 2d691963934c31242512bd4dd349d970c0350243..aa47e541ee4fe55254edc3fb59ef534ba4d5be66 100755 (executable)
@@ -151,6 +151,7 @@ save_stash () {
                        ;;
                -*)
                        echo "error: unknown option for 'stash save': $1"
+                       echo "       To provide a message, use git stash save -- '$1'"
                        usage
                        ;;
                *)
index 265852f4596bfe5aeca12be06f78631320b8ebb4..473a0b9d556fc7ead7088c4cb6c6f14c4a70995b 100755 (executable)
@@ -5459,7 +5459,12 @@ sub git_svn_log_cmd {
 
 # adapted from pager.c
 sub config_pager {
-       chomp(my $pager = command_oneline(qw(var GIT_PAGER)));
+       if (! -t *STDOUT) {
+               $ENV{GIT_PAGER_IN_USE} = 'false';
+               $pager = undef;
+               return;
+       }
+       chomp($pager = command_oneline(qw(var GIT_PAGER)));
        if ($pager eq 'cat') {
                $pager = undef;
        }
@@ -5467,7 +5472,7 @@ sub config_pager {
 }
 
 sub run_pager {
-       return unless -t *STDOUT && defined $pager;
+       return unless defined $pager;
        pipe my ($rfd, $wfd) or return;
        defined(my $pid = fork) or ::fatal "Can't fork: $!";
        if (!$pid) {
index 6c2c8e12598ca9ba17e7cc3d74812a8b7883d59a..ad6a04c464075c31afe3c67222f0bdeabc76f569 100644 (file)
@@ -312,12 +312,16 @@ If you want to have one URL for both gitweb and your http://
 repositories, you can configure apache like this:
 
 <VirtualHost *:80>
-    ServerName git.example.org
-    DocumentRoot /pub/git
-    SetEnv     GITWEB_CONFIG   /etc/gitweb.conf
+    ServerName         git.example.org
+    DocumentRoot       /pub/git
+    SetEnv                     GITWEB_CONFIG   /etc/gitweb.conf
+
+    # turning on mod rewrite
     RewriteEngine on
+
     # make the front page an internal rewrite to the gitweb script
     RewriteRule ^/$  /cgi-bin/gitweb.cgi
+
     # make access for "dumb clients" work
     RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
 </VirtualHost>
@@ -343,6 +347,63 @@ something like the following in your gitweb.conf (or gitweb_config.perl) file:
   $home_link = "/";
 
 
+Webserver configuration with multiple projects' root
+----------------------------------------------------
+
+If you want to use gitweb with several project roots you can edit your apache
+virtual host and gitweb.conf configuration files like this :
+
+virtual host configuration :
+
+<VirtualHost *:80>
+    ServerName                 git.example.org
+    DocumentRoot               /pub/git
+    SetEnv                             GITWEB_CONFIG   /etc/gitweb.conf
+
+    # turning on mod rewrite
+    RewriteEngine on
+
+    # make the front page an internal rewrite to the gitweb script
+    RewriteRule ^/$            /cgi-bin/gitweb.cgi [QSA,L,PT]
+
+    # look for a public_git folder in unix users' home
+    # http://git.example.org/~<user>/
+    RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$  /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/+<user>/
+    #RewriteRule ^/\+([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # http://git.example.org/user/<user>/
+    #RewriteRule ^/user/([^\/]+)/(gitweb.cgi)?$        /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
+
+    # defined list of project roots
+    RewriteRule ^/scm(/|/gitweb.cgi)?$         /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/pub/scm/,L,PT]
+    RewriteRule ^/var(/|/gitweb.cgi)?$         /cgi-bin/gitweb.cgi [QSA,E=GITWEB_PROJECTROOT:/var/git/,L,PT]
+
+    # make access for "dumb clients" work
+    RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI}  [L,PT]
+</VirtualHost>
+
+gitweb.conf configuration :
+
+$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
+
+These configurations enable two things. First, each unix user (<user>) of the
+server will be able to browse through gitweb git repositories found in
+~/public_git/ with the following url : http://git.example.org/~<user>/
+
+If you do not want this feature on your server just remove the second rewrite rule.
+
+If you already use mod_userdir in your virtual host or you don't want to use
+the '~' as first character just comment or remove the second rewrite rule and
+uncomment one of the following according to what you want.
+
+Second, repositories found in /pub/scm/ and /var/git/ will be accesible
+through http://git.example.org/scm/ and http://git.example.org/var/.
+You can add as many project roots as you want by adding rewrite rules like the
+third and the fourth.
+
+
 PATH_INFO usage
 -----------------------
 If you enable PATH_INFO usage in gitweb by putting
index 1f6978ac1f3ca2f915c5b87d8b196ee1e0e52aca..3d80deba01696f7d039955da68daac9e61507420 100755 (executable)
@@ -454,7 +454,11 @@ sub gitweb_get_feature {
                $feature{$name}{'sub'},
                $feature{$name}{'override'},
                @{$feature{$name}{'default'}});
-       if (!$override) { return @defaults; }
+       # project specific override is possible only if we have project
+       our $git_dir; # global variable, declared later
+       if (!$override || !defined $git_dir) {
+               return @defaults;
+       }
        if (!defined $sub) {
                warn "feature $name is not overridable";
                return @defaults;
@@ -550,11 +554,14 @@ sub filter_snapshot_fmts {
 }
 
 our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
+our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
+# die if there are errors parsing config file
 if (-e $GITWEB_CONFIG) {
        do $GITWEB_CONFIG;
-} else {
-       our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
-       do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
+       die $@ if $@;
+} elsif (-e $GITWEB_CONFIG_SYSTEM) {
+       do $GITWEB_CONFIG_SYSTEM;
+       die $@ if $@;
 }
 
 # Get loadavg of system, to compare against $maxload.
@@ -2202,6 +2209,9 @@ sub config_to_multi {
 sub git_get_project_config {
        my ($key, $type) = @_;
 
+       # do we have project
+       return unless (defined $project && defined $git_dir);
+
        # key sanity check
        return unless ($key);
        $key =~ s/^gitweb\.//;
diff --git a/grep.c b/grep.c
index a0864f1cbbe5fcc6f28eb57a08ebfd9a76c87da6..90a063a985098976f831c37227496d612fae37c0 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -11,8 +11,8 @@ void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field fie
        p->no = 0;
        p->token = GREP_PATTERN_HEAD;
        p->field = field;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
+       *opt->header_tail = p;
+       opt->header_tail = &p->next;
        p->next = NULL;
 }
 
@@ -184,9 +184,26 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
 void compile_grep_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
-
-       if (opt->all_match)
-               opt->extended = 1;
+       struct grep_expr *header_expr = NULL;
+
+       if (opt->header_list) {
+               p = opt->header_list;
+               header_expr = compile_pattern_expr(&p);
+               if (p)
+                       die("incomplete pattern expression: %s", p->pattern);
+               for (p = opt->header_list; p; p = p->next) {
+                       switch (p->token) {
+                       case GREP_PATTERN: /* atom */
+                       case GREP_PATTERN_HEAD:
+                       case GREP_PATTERN_BODY:
+                               compile_regexp(p, opt);
+                               break;
+                       default:
+                               opt->extended = 1;
+                               break;
+                       }
+               }
+       }
 
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
@@ -201,7 +218,9 @@ void compile_grep_patterns(struct grep_opt *opt)
                }
        }
 
-       if (!opt->extended)
+       if (opt->all_match || header_expr)
+               opt->extended = 1;
+       else if (!opt->extended)
                return;
 
        /* Then bundle them up in an expression.
@@ -212,6 +231,21 @@ void compile_grep_patterns(struct grep_opt *opt)
                opt->pattern_expression = compile_pattern_expr(&p);
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
+
+       if (!header_expr)
+               return;
+
+       if (opt->pattern_expression) {
+               struct grep_expr *z;
+               z = xcalloc(1, sizeof(*z));
+               z->node = GREP_NODE_OR;
+               z->u.binary.left = opt->pattern_expression;
+               z->u.binary.right = header_expr;
+               opt->pattern_expression = z;
+       } else {
+               opt->pattern_expression = header_expr;
+       }
+       opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
diff --git a/grep.h b/grep.h
index 970308799664fe6a3871c0b8364c13e43cf96e1f..d35bc29bfd76f27c066f40dcb3f31078b4100059 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -59,6 +59,8 @@ struct grep_expr {
 struct grep_opt {
        struct grep_pat *pattern_list;
        struct grep_pat **pattern_tail;
+       struct grep_pat *header_list;
+       struct grep_pat **header_tail;
        struct grep_expr *pattern_expression;
        const char *prefix;
        int prefix_length;
index ba72fa4b6e2fcebc74e611ed1ca200a37b9f339d..5631930bc3462c5d85d29e7e840f1cf24cd7111b 100644 (file)
@@ -91,7 +91,6 @@ struct msg_data {
        char *data;
        int len;
        unsigned char flags;
-       unsigned int crlf:1;
 };
 
 static const char imap_send_usage[] = "git imap-send < <mbox>";
@@ -1162,6 +1161,44 @@ static int imap_make_flags(int flags, char *buf)
        return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+       char *new;
+       int i, j, lfnum = 0;
+
+       if (msg->data[0] == '\n')
+               lfnum++;
+       for (i = 1; i < msg->len; i++) {
+               if (msg->data[i - 1] != '\r' && msg->data[i] == '\n')
+                       lfnum++;
+       }
+
+       new = xmalloc(msg->len + lfnum);
+       if (msg->data[0] == '\n') {
+               new[0] = '\r';
+               new[1] = '\n';
+               i = 1;
+               j = 2;
+       } else {
+               new[0] = msg->data[0];
+               i = 1;
+               j = 1;
+       }
+       for ( ; i < msg->len; i++) {
+               if (msg->data[i] != '\n') {
+                       new[j++] = msg->data[i];
+                       continue;
+               }
+               if (msg->data[i - 1] != '\r')
+                       new[j++] = '\r';
+               /* otherwise it already had CR before */
+               new[j++] = '\n';
+       }
+       msg->len += lfnum;
+       free(msg->data);
+       msg->data = new;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
        struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1171,6 +1208,7 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
        int ret, d;
        char flagstr[128];
 
+       lf_to_crlf(data);
        memset(&cb, 0, sizeof(cb));
 
        cb.dlen = data->len;
diff --git a/pager.c b/pager.c
index 2c7e8ecb3c0860b00113e348008415ca28a7ff72..dac358f047550a07dd8d90b2e410feab3eebd534 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -48,11 +48,11 @@ static void wait_for_pager_signal(int signo)
        raise(signo);
 }
 
-const char *git_pager(void)
+const char *git_pager(int stdout_is_tty)
 {
        const char *pager;
 
-       if (!isatty(1))
+       if (!stdout_is_tty)
                return NULL;
 
        pager = getenv("GIT_PAGER");
@@ -73,7 +73,7 @@ const char *git_pager(void)
 
 void setup_pager(void)
 {
-       const char *pager = git_pager();
+       const char *pager = git_pager(isatty(1));
 
        if (!pager)
                return;
diff --git a/path.c b/path.c
index e166d5380e47a7f6560f504de6550a508de1e170..aaa9345ebca4140317d8509ba95ec61cff82192d 100644 (file)
--- a/path.c
+++ b/path.c
@@ -157,6 +157,85 @@ int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
        return mkstemps(path, suffix_len);
 }
 
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
+{
+       static const char letters[] =
+               "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "0123456789";
+       static const int num_letters = 62;
+       uint64_t value;
+       struct timeval tv;
+       char *template;
+       size_t len;
+       int fd, count;
+
+       len = strlen(pattern);
+
+       if (len < 6 + suffix_len) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /*
+        * Replace pattern's XXXXXX characters with randomness.
+        * Try TMP_MAX different filenames.
+        */
+       gettimeofday(&tv, NULL);
+       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       template = &pattern[len - 6 - suffix_len];
+       for (count = 0; count < TMP_MAX; ++count) {
+               uint64_t v = value;
+               /* Fill in the random bits. */
+               template[0] = letters[v % num_letters]; v /= num_letters;
+               template[1] = letters[v % num_letters]; v /= num_letters;
+               template[2] = letters[v % num_letters]; v /= num_letters;
+               template[3] = letters[v % num_letters]; v /= num_letters;
+               template[4] = letters[v % num_letters]; v /= num_letters;
+               template[5] = letters[v % num_letters]; v /= num_letters;
+
+               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+               if (fd > 0)
+                       return fd;
+               /*
+                * Fatal error (EPERM, ENOSPC etc).
+                * It doesn't make sense to loop.
+                */
+               if (errno != EEXIST)
+                       break;
+               /*
+                * This is a random value.  It is only necessary that
+                * the next TMP_MAX values generated by adding 7777 to
+                * VALUE are different with (module 2^32).
+                */
+               value += 7777;
+       }
+       /* We return the null string if we can't find a unique file name.  */
+       pattern[0] = '\0';
+       return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+       /* mkstemp is just mkstemps with no suffix */
+       return git_mkstemps_mode(pattern, 0, mode);
+}
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+       return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
 int validate_headref(const char *path)
 {
        struct stat st;
index a904164e425097380781c1ecd9bb13f4feec0de6..d38812085151b6738e7ca95be10968b973bf13b4 100644 (file)
@@ -184,13 +184,13 @@ static struct discovery* discover_refs(const char *service)
        return last;
 }
 
-static int write_discovery(int fd, void *data)
+static int write_discovery(int in, int out, void *data)
 {
        struct discovery *heads = data;
        int err = 0;
-       if (write_in_full(fd, heads->buf, heads->len) != heads->len)
+       if (write_in_full(out, heads->buf, heads->len) != heads->len)
                err = 1;
-       close(fd);
+       close(out);
        return err;
 }
 
@@ -202,6 +202,7 @@ static struct ref *parse_git_refs(struct discovery *heads)
        memset(&async, 0, sizeof(async));
        async.proc = write_discovery;
        async.data = heads;
+       async.out = -1;
 
        if (start_async(&async))
                die("cannot start thread to parse advertised refs");
index d1d3e753955146cadfaf6da274487a4a369f0521..a59f74f76c293efa783103eaf3d167c97b3768ea 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -364,7 +364,7 @@ static int find_conflict(struct string_list *conflict)
 static int merge(const char *name, const char *path)
 {
        int ret;
-       mmfile_t cur, base, other;
+       mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0};
        mmbuffer_t result = {NULL, 0};
 
        if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0)
@@ -372,8 +372,10 @@ static int merge(const char *name, const char *path)
 
        if (read_mmfile(&cur, rerere_path(name, "thisimage")) ||
                        read_mmfile(&base, rerere_path(name, "preimage")) ||
-                       read_mmfile(&other, rerere_path(name, "postimage")))
-               return 1;
+                       read_mmfile(&other, rerere_path(name, "postimage"))) {
+               ret = 1;
+               goto out;
+       }
        ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0);
        if (!ret) {
                FILE *f = fopen(path, "w");
@@ -387,6 +389,7 @@ static int merge(const char *name, const char *path)
                                     strerror(errno));
        }
 
+out:
        free(cur.ptr);
        free(base.ptr);
        free(other.ptr);
index 3ba6d991f6e9789949c314c2981dfc6b208a6f66..29721ecf84316d01b18ffdee8bdefa8dbb3887db 100644 (file)
@@ -547,6 +547,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
                        right_count++;
        }
 
+       if (!left_count || !right_count)
+               return;
+
        left_first = left_count < right_count;
        init_patch_ids(&ids);
        if (revs->diffopt.nr_paths) {
@@ -823,6 +826,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
 
        revs->grep_filter.status_only = 1;
        revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+       revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
 
        diff_setup(&revs->diffopt);
@@ -1801,7 +1805,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
-       if (!opt->grep_filter.pattern_list)
+       if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
                           NULL, /* we say nothing, not even filename */
index 2feb493951322617692085998ac8507cdba9dd30..0cd7f02ffe597d3708873d4f3d6b4c0e88e8fae7 100644 (file)
@@ -233,6 +233,9 @@ fail_pipe:
                else if (need_err) {
                        dup2(fderr[1], 2);
                        close_pair(fderr);
+               } else if (cmd->err > 1) {
+                       dup2(cmd->err, 2);
+                       close(cmd->err);
                }
 
                if (cmd->no_stdout)
@@ -325,6 +328,8 @@ fail_pipe:
                fherr = open("/dev/null", O_RDWR);
        else if (need_err)
                fherr = dup(fderr[1]);
+       else if (cmd->err > 2)
+               fherr = dup(cmd->err);
 
        if (cmd->no_stdout)
                fhout = open("/dev/null", O_RDWR);
@@ -394,6 +399,8 @@ fail_pipe:
 
        if (need_err)
                close(fderr[1]);
+       else if (cmd->err)
+               close(cmd->err);
 
        return 0;
 }
@@ -444,17 +451,51 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
 static unsigned __stdcall run_thread(void *data)
 {
        struct async *async = data;
-       return async->proc(async->fd_for_proc, async->data);
+       return async->proc(async->proc_in, async->proc_out, async->data);
 }
 #endif
 
 int start_async(struct async *async)
 {
-       int pipe_out[2];
+       int need_in, need_out;
+       int fdin[2], fdout[2];
+       int proc_in, proc_out;
 
-       if (pipe(pipe_out) < 0)
-               return error("cannot create pipe: %s", strerror(errno));
-       async->out = pipe_out[0];
+       need_in = async->in < 0;
+       if (need_in) {
+               if (pipe(fdin) < 0) {
+                       if (async->out > 0)
+                               close(async->out);
+                       return error("cannot create pipe: %s", strerror(errno));
+               }
+               async->in = fdin[1];
+       }
+
+       need_out = async->out < 0;
+       if (need_out) {
+               if (pipe(fdout) < 0) {
+                       if (need_in)
+                               close_pair(fdin);
+                       else if (async->in)
+                               close(async->in);
+                       return error("cannot create pipe: %s", strerror(errno));
+               }
+               async->out = fdout[0];
+       }
+
+       if (need_in)
+               proc_in = fdin[0];
+       else if (async->in)
+               proc_in = async->in;
+       else
+               proc_in = -1;
+
+       if (need_out)
+               proc_out = fdout[1];
+       else if (async->out)
+               proc_out = async->out;
+       else
+               proc_out = -1;
 
 #ifndef WIN32
        /* Flush stdio before fork() to avoid cloning buffers */
@@ -463,24 +504,47 @@ int start_async(struct async *async)
        async->pid = fork();
        if (async->pid < 0) {
                error("fork (async) failed: %s", strerror(errno));
-               close_pair(pipe_out);
-               return -1;
+               goto error;
        }
        if (!async->pid) {
-               close(pipe_out[0]);
-               exit(!!async->proc(pipe_out[1], async->data));
+               if (need_in)
+                       close(fdin[1]);
+               if (need_out)
+                       close(fdout[0]);
+               exit(!!async->proc(proc_in, proc_out, async->data));
        }
-       close(pipe_out[1]);
+
+       if (need_in)
+               close(fdin[0]);
+       else if (async->in)
+               close(async->in);
+
+       if (need_out)
+               close(fdout[1]);
+       else if (async->out)
+               close(async->out);
 #else
-       async->fd_for_proc = pipe_out[1];
+       async->proc_in = proc_in;
+       async->proc_out = proc_out;
        async->tid = (HANDLE) _beginthreadex(NULL, 0, run_thread, async, 0, NULL);
        if (!async->tid) {
                error("cannot create thread: %s", strerror(errno));
-               close_pair(pipe_out);
-               return -1;
+               goto error;
        }
 #endif
        return 0;
+
+error:
+       if (need_in)
+               close_pair(fdin);
+       else if (async->in)
+               close(async->in);
+
+       if (need_out)
+               close_pair(fdout);
+       else if (async->out)
+               close(async->out);
+       return -1;
 }
 
 int finish_async(struct async *async)
index 967ba8cc09786934724132b629587419f195b245..94619f52d95888b320664b7f19db3eeb7d6d8cca 100644 (file)
@@ -18,7 +18,7 @@ struct child_process {
         * - Specify > 0 to set a channel to a particular FD as follows:
         *     .in: a readable FD, becomes child's stdin
         *     .out: a writable FD, becomes child's stdout/stderr
-        *     .err > 0 not supported
+        *     .err: a writable FD, becomes child's stderr
         *   The specified FD is closed by start_command(), even in case
         *   of errors!
         */
@@ -66,17 +66,20 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
  */
 struct async {
        /*
-        * proc writes to fd and closes it;
+        * proc reads from in; closes it before return
+        * proc writes to out; closes it before return
         * returns 0 on success, non-zero on failure
         */
-       int (*proc)(int fd, void *data);
+       int (*proc)(int in, int out, void *data);
        void *data;
+       int in;         /* caller writes here and closes it */
        int out;        /* caller reads from here and closes it */
 #ifndef WIN32
        pid_t pid;
 #else
        HANDLE tid;
-       int fd_for_proc;
+       int proc_in;
+       int proc_out;
 #endif
 };
 
index 657825e14ef78c19649779fe89e1d09ae672901a..c23cc5e6e19a2d8c9a92161b0a5d62a5ef8e920b 100644 (file)
@@ -2206,7 +2206,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
        }
 
 out:
-       if (set_shared_perm(filename, (S_IFREG|0444)))
+       if (adjust_shared_perm(filename))
                return error("unable to set permission to '%s'", filename);
        return 0;
 }
@@ -2262,7 +2262,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
        }
        memcpy(buffer, filename, dirlen);
        strcpy(buffer + dirlen, "tmp_obj_XXXXXX");
-       fd = mkstemp(buffer);
+       fd = git_mkstemp_mode(buffer, 0444);
        if (fd < 0 && dirlen && errno == ENOENT) {
                /* Make sure the directory exists */
                memcpy(buffer, filename, dirlen);
@@ -2272,7 +2272,7 @@ static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename)
 
                /* Try again */
                strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
-               fd = mkstemp(buffer);
+               fd = git_mkstemp_mode(buffer, 0444);
        }
        return fd;
 }
@@ -2281,9 +2281,10 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
                              void *buf, unsigned long len, time_t mtime)
 {
        int fd, ret;
-       size_t size;
-       unsigned char *compressed;
+       unsigned char compressed[4096];
        z_stream stream;
+       git_SHA_CTX c;
+       unsigned char parano_sha1[20];
        char *filename;
        static char tmpfile[PATH_MAX];
 
@@ -2301,36 +2302,40 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
        deflateInit(&stream, zlib_compression_level);
-       size = 8 + deflateBound(&stream, len+hdrlen);
-       compressed = xmalloc(size);
-
-       /* Compress it */
        stream.next_out = compressed;
-       stream.avail_out = size;
+       stream.avail_out = sizeof(compressed);
+       git_SHA1_Init(&c);
 
        /* First header.. */
        stream.next_in = (unsigned char *)hdr;
        stream.avail_in = hdrlen;
        while (deflate(&stream, 0) == Z_OK)
                /* nothing */;
+       git_SHA1_Update(&c, hdr, hdrlen);
 
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
-       ret = deflate(&stream, Z_FINISH);
+       do {
+               unsigned char *in0 = stream.next_in;
+               ret = deflate(&stream, Z_FINISH);
+               git_SHA1_Update(&c, in0, stream.next_in - in0);
+               if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
+                       die("unable to write sha1 file");
+               stream.next_out = compressed;
+               stream.avail_out = sizeof(compressed);
+       } while (ret == Z_OK);
+
        if (ret != Z_STREAM_END)
                die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
-
        ret = deflateEnd(&stream);
        if (ret != Z_OK)
                die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+       git_SHA1_Final(parano_sha1, &c);
+       if (hashcmp(sha1, parano_sha1) != 0)
+               die("confused by unstable object source data for %s", sha1_to_hex(sha1));
 
-       size = stream.total_out;
-
-       if (write_buffer(fd, compressed, size) < 0)
-               die("unable to write sha1 file");
        close_sha1_file(fd);
-       free(compressed);
 
        if (mtime) {
                struct utimbuf utb;
@@ -2434,6 +2439,8 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        return ret;
 }
 
+#define SMALL_FILE_SIZE (32*1024)
+
 int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
             enum object_type type, const char *path)
 {
@@ -2448,6 +2455,14 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                else
                        ret = -1;
                strbuf_release(&sbuf);
+       } else if (size <= SMALL_FILE_SIZE) {
+               char *buf = xmalloc(size);
+               if (size == read_in_full(fd, buf, size))
+                       ret = index_mem(sha1, buf, size, write_object, type,
+                                       path);
+               else
+                       ret = error("short read %s", strerror(errno));
+               free(buf);
        } else if (size) {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
                ret = index_mem(sha1, buf, size, write_object, type, path);
index 43884c69b350426b46ed73ffcfd5fc29cf1da1b1..bf924178380c42a50b2fd7a02ff869dcc3ddff6a 100644 (file)
@@ -992,13 +992,15 @@ static void diagnose_invalid_index_path(int stage,
        pos = cache_name_pos(filename, namelen);
        if (pos < 0)
                pos = -pos - 1;
-       ce = active_cache[pos];
-       if (ce_namelen(ce) == namelen &&
-           !memcmp(ce->name, filename, namelen))
-               die("Path '%s' is in the index, but not at stage %d.\n"
-                   "Did you mean ':%d:%s'?",
-                   filename, stage,
-                   ce_stage(ce), filename);
+       if (pos < active_nr) {
+               ce = active_cache[pos];
+               if (ce_namelen(ce) == namelen &&
+                   !memcmp(ce->name, filename, namelen))
+                       die("Path '%s' is in the index, but not at stage %d.\n"
+                           "Did you mean ':%d:%s'?",
+                           filename, stage,
+                           ce_stage(ce), filename);
+       }
 
        /* Confusion between relative and absolute filenames? */
        fullnamelen = namelen + strlen(prefix);
@@ -1008,13 +1010,15 @@ static void diagnose_invalid_index_path(int stage,
        pos = cache_name_pos(fullname, fullnamelen);
        if (pos < 0)
                pos = -pos - 1;
-       ce = active_cache[pos];
-       if (ce_namelen(ce) == fullnamelen &&
-           !memcmp(ce->name, fullname, fullnamelen))
-               die("Path '%s' is in the index, but not '%s'.\n"
-                   "Did you mean ':%d:%s'?",
-                   fullname, filename,
-                   ce_stage(ce), fullname);
+       if (pos < active_nr) {
+               ce = active_cache[pos];
+               if (ce_namelen(ce) == fullnamelen &&
+                   !memcmp(ce->name, fullname, fullnamelen))
+                       die("Path '%s' is in the index, but not '%s'.\n"
+                           "Did you mean ':%d:%s'?",
+                           fullname, filename,
+                           ce_stage(ce), fullname);
+       }
 
        if (!lstat(filename, &st))
                die("Path '%s' exists on disk, but not in the index.", filename);
old mode 100755 (executable)
new mode 100644 (file)
index 75a3ee2..ce36f34
@@ -1,3 +1,5 @@
+: included from t2016 and others
+
 . ./test-lib.sh
 
 if ! test_have_prereq PERL; then
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
new file mode 100755 (executable)
index 0000000..cc30be4
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Matthieu Moy
+#
+
+test_description='Test repository with default ACL'
+
+# Create the test repo with restrictive umask
+# => this must come before . ./test-lib.sh
+umask 077
+
+. ./test-lib.sh
+
+# We need an arbitrary other user give permission to using ACLs. root
+# is a good candidate: exists on all unices, and it has permission
+# anyway, so we don't create a security hole running the testsuite.
+
+if ! setfacl -m u:root:rwx .; then
+    say "Skipping ACL tests: unable to use setfacl"
+    test_done
+fi
+
+modebits () {
+       ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
+}
+
+check_perms_and_acl () {
+       actual=$(modebits "$1") &&
+       case "$actual" in
+       -r--r-----*)
+               : happy
+               ;;
+       *)
+               echo "Got permission '$actual', expected '-r--r-----'"
+               false
+               ;;
+       esac &&
+       getfacl "$1" > actual &&
+       grep -q "user:root:rwx" actual &&
+       grep -q "user:${LOGNAME}:rwx" actual &&
+       grep -q "mask::r--" actual &&
+       grep -q "group::---" actual || false
+}
+
+dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
+
+test_expect_success 'Setup test repo' '
+       setfacl -m u:root:rwx          $dirs_to_set &&
+       setfacl -d -m u:"$LOGNAME":rwx $dirs_to_set &&
+       setfacl -d -m u:root:rwx       $dirs_to_set &&
+
+       touch file.txt &&
+       git add file.txt &&
+       git commit -m "init"
+'
+
+test_expect_success 'Objects creation does not break ACLs with restrictive umask' '
+       # SHA1 for empty blob
+       check_perms_and_acl .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+'
+
+test_expect_success 'git gc does not break ACLs with restrictive umask' '
+       git gc &&
+       check_perms_and_acl .git/objects/pack/*.pack
+'
+
+test_done
index 4d1c2e9e099918b5ff4e2e9458e702546f4c9605..2144184d790055ab12dfbb53ec10f519de598f72 100755 (executable)
@@ -66,6 +66,14 @@ test_expect_success 'git checkout -p HEAD^' '
        verify_state dir/foo parent parent
 '
 
+test_expect_success 'git checkout -p handles deletion' '
+       set_state dir/foo work index &&
+       rm dir/foo &&
+       (echo n; echo y) | git checkout -p &&
+       verify_saved_state bar &&
+       verify_state dir/foo index index
+'
+
 # The idea in the rest is that bar sorts first, so we always say 'y'
 # first and if the path limiter fails it'll apply to bar instead of
 # dir/foo.  There's always an extra 'n' to reject edits to dir/foo in
index 5d9604b8155401a2e345a69a5d030cffd7d16818..714626d2d61ea95465b838595fe9b5e32b8f55b1 100755 (executable)
@@ -8,6 +8,7 @@ test_description='Test commit notes'
 . ./test-lib.sh
 
 cat > fake_editor.sh << \EOF
+#!/bin/sh
 echo "$MSG" > "$1"
 echo "$MSG" >& 2
 EOF
index 60dd2014d5ae5d5e9e168b8b60278d90ef93cc53..0391a5827ea8ba196b7796b7df818f8ac860c387 100755 (executable)
@@ -5,6 +5,9 @@ test_description='Return value of diffs'
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+       echo "1 " >a &&
+       git add . &&
+       git commit -m zeroth &&
        echo 1 >a &&
        git add . &&
        git commit -m first &&
@@ -13,6 +16,18 @@ test_expect_success 'setup' '
        git commit -a -m second
 '
 
+test_expect_success 'git diff --quiet -w  HEAD^^ HEAD^' '
+       git diff --quiet -w HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet HEAD^^ HEAD^' '
+       test_must_fail git diff --quiet HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet -w  HEAD^ HEAD' '
+       test_must_fail git diff --quiet -w HEAD^ HEAD
+'
+
 test_expect_success 'git diff-tree HEAD^ HEAD' '
        git diff-tree --exit-code HEAD^ HEAD
        test $? = 1
index 9577238685acecd1b5d530ffa3f4f03eef25b9b6..4abb3d5c6c4884806cdaabcc0b01acb9c1263af2 100644 (file)
@@ -1,2 +1,2 @@
-- a list
+  - a list
   - of stuff
index 325714e5299a5b59157c3741e7fa0d98d70b9990..17bcb0b04096eabb29c13a025fd2afe8d1e4623b 100755 (executable)
@@ -17,23 +17,22 @@ test_expect_success setup '
        commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
        git update-ref refs/heads/master $commit0 &&
        git update-ref refs/heads/tofail $commit1 &&
-       git clone ./. victim &&
-       GIT_DIR=victim/.git git config receive.denyCurrentBranch warn &&
-       GIT_DIR=victim/.git git update-ref refs/heads/tofail $commit1 &&
+       git clone --bare ./. victim.git &&
+       GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
        git update-ref refs/heads/master $commit1 &&
        git update-ref refs/heads/tofail $commit0
 '
 
-cat >victim/.git/hooks/pre-receive <<'EOF'
+cat >victim.git/hooks/pre-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/pre-receive.args
 cat - >$GIT_DIR/pre-receive.stdin
 echo STDOUT pre-receive
 echo STDERR pre-receive >&2
 EOF
-chmod u+x victim/.git/hooks/pre-receive
+chmod u+x victim.git/hooks/pre-receive
 
-cat >victim/.git/hooks/update <<'EOF'
+cat >victim.git/hooks/update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/update.args
 read x; printf %s "$x" >$GIT_DIR/update.stdin
@@ -41,77 +40,77 @@ echo STDOUT update $1
 echo STDERR update $1 >&2
 test "$1" = refs/heads/master || exit
 EOF
-chmod u+x victim/.git/hooks/update
+chmod u+x victim.git/hooks/update
 
-cat >victim/.git/hooks/post-receive <<'EOF'
+cat >victim.git/hooks/post-receive <<'EOF'
 #!/bin/sh
 printf %s "$@" >>$GIT_DIR/post-receive.args
 cat - >$GIT_DIR/post-receive.stdin
 echo STDOUT post-receive
 echo STDERR post-receive >&2
 EOF
-chmod u+x victim/.git/hooks/post-receive
+chmod u+x victim.git/hooks/post-receive
 
-cat >victim/.git/hooks/post-update <<'EOF'
+cat >victim.git/hooks/post-update <<'EOF'
 #!/bin/sh
 echo "$@" >>$GIT_DIR/post-update.args
 read x; printf %s "$x" >$GIT_DIR/post-update.stdin
 echo STDOUT post-update
 echo STDERR post-update >&2
 EOF
-chmod u+x victim/.git/hooks/post-update
+chmod u+x victim.git/hooks/post-update
 
 test_expect_success push '
-       test_must_fail git send-pack --force ./victim/.git \
+       test_must_fail git send-pack --force ./victim.git \
                master tofail >send.out 2>send.err
 '
 
 test_expect_success 'updated as expected' '
-       test $(GIT_DIR=victim/.git git rev-parse master) = $commit1 &&
-       test $(GIT_DIR=victim/.git git rev-parse tofail) = $commit1
+       test $(GIT_DIR=victim.git git rev-parse master) = $commit1 &&
+       test $(GIT_DIR=victim.git git rev-parse tofail) = $commit1
 '
 
 test_expect_success 'hooks ran' '
-       test -f victim/.git/pre-receive.args &&
-       test -f victim/.git/pre-receive.stdin &&
-       test -f victim/.git/update.args &&
-       test -f victim/.git/update.stdin &&
-       test -f victim/.git/post-receive.args &&
-       test -f victim/.git/post-receive.stdin &&
-       test -f victim/.git/post-update.args &&
-       test -f victim/.git/post-update.stdin
+       test -f victim.git/pre-receive.args &&
+       test -f victim.git/pre-receive.stdin &&
+       test -f victim.git/update.args &&
+       test -f victim.git/update.stdin &&
+       test -f victim.git/post-receive.args &&
+       test -f victim.git/post-receive.stdin &&
+       test -f victim.git/post-update.args &&
+       test -f victim.git/post-update.stdin
 '
 
 test_expect_success 'pre-receive hook input' '
        (echo $commit0 $commit1 refs/heads/master;
         echo $commit1 $commit0 refs/heads/tofail
-       ) | test_cmp - victim/.git/pre-receive.stdin
+       ) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
        (echo refs/heads/master $commit0 $commit1;
         echo refs/heads/tofail $commit1 $commit0
-       ) | test_cmp - victim/.git/update.args
+       ) | test_cmp - victim.git/update.args
 '
 
 test_expect_success 'post-receive hook input' '
        echo $commit0 $commit1 refs/heads/master |
-       test_cmp - victim/.git/post-receive.stdin
+       test_cmp - victim.git/post-receive.stdin
 '
 
 test_expect_success 'post-update hook arguments' '
        echo refs/heads/master |
-       test_cmp - victim/.git/post-update.args
+       test_cmp - victim.git/post-update.args
 '
 
 test_expect_success 'all hook stdin is /dev/null' '
-       ! test -s victim/.git/update.stdin &&
-       ! test -s victim/.git/post-update.stdin
+       ! test -s victim.git/update.stdin &&
+       ! test -s victim.git/post-update.stdin
 '
 
 test_expect_success 'all *-receive hook args are empty' '
-       ! test -s victim/.git/pre-receive.args &&
-       ! test -s victim/.git/post-receive.args
+       ! test -s victim.git/pre-receive.args &&
+       ! test -s victim.git/post-receive.args
 '
 
 test_expect_success 'send-pack produced no output' '
@@ -119,20 +118,21 @@ test_expect_success 'send-pack produced no output' '
 '
 
 cat <<EOF >expect
-STDOUT pre-receive
-STDERR pre-receive
-STDOUT update refs/heads/master
-STDERR update refs/heads/master
-STDOUT update refs/heads/tofail
-STDERR update refs/heads/tofail
-STDOUT post-receive
-STDERR post-receive
-STDOUT post-update
-STDERR post-update
+remote: STDOUT pre-receive
+remote: STDERR pre-receive
+remote: STDOUT update refs/heads/master
+remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/tofail
+remote: STDERR update refs/heads/tofail
+remote: error: hook declined to update refs/heads/tofail
+remote: STDOUT post-receive
+remote: STDERR post-receive
+remote: STDOUT post-update
+remote: STDERR post-update
 EOF
 test_expect_success 'send-pack stderr contains hook messages' '
-       grep ^STD send.err >actual &&
-       test_cmp - actual <expect
+       grep ^remote: send.err | sed "s/ *\$//" >actual &&
+       test_cmp expect actual
 '
 
 test_done
old mode 100755 (executable)
new mode 100644 (file)
index f55627b..985d517
@@ -1,3 +1,5 @@
+: included from 6002 and others
+
 [ -d .git/refs/tags ] || mkdir -p .git/refs/tags
 
 :> sed.script
index c51865fdbc0a6fd98cca4a4accd35b302e5fd739..3b042aacd63f77651fdaf3d10b65f4fc85669a75 100755 (executable)
@@ -567,6 +567,11 @@ test_expect_success 'skipping away from skipped commit' '
        test "$para3" = "$PARA_HASH3"
 '
 
+test_expect_success 'erroring out when using bad path parameters' '
+       test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
+       grep "bad path parameters" error.txt
+'
+
 #
 #
 test_done
index 0b583cbfc15ae09c030c0d4e82635f244ffc5293..af63d6ec6dfdf34b7a8ab4c19547d1d84cc184f2 100755 (executable)
@@ -353,7 +353,7 @@ test_expect_success 'log grep (4)' '
 '
 
 test_expect_success 'log grep (5)' '
-       git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
+       git log --author=Thor -F --pretty=tformat:%s >actual &&
        ( echo third ; echo initial ) >expect &&
        test_cmp expect actual
 '
@@ -364,6 +364,14 @@ test_expect_success 'log grep (6)' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --grep --author implicitly uses all-match' '
+       # grep matches initial and second but not third
+       # author matches only initial and third
+       git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+       echo initial >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
new file mode 100755 (executable)
index 0000000..d9202d5
--- /dev/null
@@ -0,0 +1,176 @@
+#!/bin/sh
+
+test_description='Test automatic use of a pager.'
+
+. ./test-lib.sh
+
+rm -f stdout_is_tty
+test_expect_success 'set up terminal for tests' '
+       if test -t 1
+       then
+               : > stdout_is_tty
+       elif
+               test_have_prereq PERL &&
+               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
+                       sh -c "test -t 1"
+       then
+               : > test_terminal_works
+       fi
+'
+
+if test -e stdout_is_tty
+then
+       test_terminal() { "$@"; }
+       test_set_prereq TTY
+elif test -e test_terminal_works
+then
+       test_terminal() {
+               "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl "$@"
+       }
+       test_set_prereq TTY
+else
+       say no usable terminal, so skipping some tests
+fi
+
+unset GIT_PAGER GIT_PAGER_IN_USE
+git config --unset core.pager
+PAGER='cat > paginated.out'
+export PAGER
+
+test_expect_success 'setup' '
+       test_commit initial
+'
+
+rm -f paginated.out
+test_expect_success TTY 'some commands use a pager' '
+       test_terminal git log &&
+       test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'some commands do not use a pager' '
+       test_terminal git rev-list HEAD &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success 'no pager when stdout is a pipe' '
+       git log | cat &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success 'no pager when stdout is a regular file' '
+       git log > file &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'git --paginate rev-list uses a pager' '
+       test_terminal git --paginate rev-list HEAD &&
+       test -e paginated.out
+'
+
+rm -f file paginated.out
+test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+       git --paginate log | cat &&
+       ! test -e paginated.out
+'
+
+rm -f paginated.out
+test_expect_success TTY 'no pager with --no-pager' '
+       test_terminal git --no-pager log &&
+       ! test -e paginated.out
+'
+
+# A colored commit log will begin with an appropriate ANSI escape
+# for the first color; the text "commit" comes later.
+colorful() {
+       read firstline < $1
+       ! expr "$firstline" : "^[a-zA-Z]" >/dev/null
+}
+
+rm -f colorful.log colorless.log
+test_expect_success 'tests can detect color' '
+       git log --no-color > colorless.log &&
+       git log --color > colorful.log &&
+       ! colorful colorless.log &&
+       colorful colorful.log
+'
+
+rm -f colorless.log
+git config color.ui auto
+test_expect_success 'no color when stdout is a regular file' '
+       git log > colorless.log &&
+       ! colorful colorless.log
+'
+
+rm -f paginated.out
+git config color.ui auto
+test_expect_success TTY 'color when writing to a pager' '
+       TERM=vt100 test_terminal git log &&
+       colorful paginated.out
+'
+
+rm -f colorful.log
+git config color.ui auto
+test_expect_success 'color when writing to a file intended for a pager' '
+       TERM=vt100 GIT_PAGER_IN_USE=true git log > colorful.log &&
+       colorful colorful.log
+'
+
+unset PAGER GIT_PAGER
+git config --unset core.pager
+test_expect_success 'determine default pager' '
+       less=$(git var GIT_PAGER) &&
+       test -n "$less"
+'
+
+if expr "$less" : '^[a-z]*$' > /dev/null && test_have_prereq TTY
+then
+       test_set_prereq SIMPLEPAGER
+fi
+
+unset PAGER GIT_PAGER
+git config --unset core.pager
+rm -f default_pager_used
+test_expect_success SIMPLEPAGER 'default pager is used by default' '
+       cat > $less <<-EOF &&
+       #!$SHELL_PATH
+       wc > default_pager_used
+       EOF
+       chmod +x $less &&
+       PATH=.:$PATH test_terminal git log &&
+       test -e default_pager_used
+'
+
+unset GIT_PAGER
+git config --unset core.pager
+rm -f PAGER_used
+test_expect_success TTY 'PAGER overrides default pager' '
+       PAGER="wc > PAGER_used" &&
+       export PAGER &&
+       test_terminal git log &&
+       test -e PAGER_used
+'
+
+unset GIT_PAGER
+rm -f core.pager_used
+test_expect_success TTY 'core.pager overrides PAGER' '
+       PAGER=wc &&
+       export PAGER &&
+       git config core.pager "wc > core.pager_used" &&
+       test_terminal git log &&
+       test -e core.pager_used
+'
+
+rm -f GIT_PAGER_used
+test_expect_success TTY 'GIT_PAGER overrides core.pager' '
+       git config core.pager wc &&
+       GIT_PAGER="wc > GIT_PAGER_used" &&
+       export GIT_PAGER &&
+       test_terminal git log &&
+       test -e GIT_PAGER_used
+'
+
+test_done
diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl
new file mode 100755 (executable)
index 0000000..73ff809
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdout redirected to $out.
+sub start_child {
+       my ($argv, $out) = @_;
+       my $pid = fork;
+       if (not defined $pid) {
+               die "fork failed: $!"
+       } elsif ($pid == 0) {
+               open STDOUT, ">&", $out;
+               close $out;
+               exec(@$argv) or die "cannot exec '$argv->[0]': $!"
+       }
+       return $pid;
+}
+
+# Wait for $pid to finish.
+sub finish_child {
+       # Simplified from wait_or_whine() in run-command.c.
+       my ($pid) = @_;
+
+       my $waiting = waitpid($pid, 0);
+       if ($waiting < 0) {
+               die "waitpid failed: $!";
+       } elsif ($? & 127) {
+               my $code = $? & 127;
+               warn "died of signal $code";
+               return $code - 128;
+       } else {
+               return $? >> 8;
+       }
+}
+
+sub xsendfile {
+       my ($out, $in) = @_;
+
+       # Note: the real sendfile() cannot read from a terminal.
+
+       # It is unspecified by POSIX whether reads
+       # from a disconnected terminal will return
+       # EIO (as in AIX 4.x, IRIX, and Linux) or
+       # end-of-file.  Either is fine.
+       copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+}
+
+if ($#ARGV < 1) {
+       die "usage: test-terminal program args";
+}
+my $master = new IO::Pty;
+my $slave = $master->slave;
+my $pid = start_child(\@ARGV, $slave);
+close $slave;
+xsendfile(\*STDOUT, $master);
+exit(finish_child($pid));
index 6442f710be8bcaea11931044d52deb5b75d8f7e0..d20ed61b481539b25c054c49bd82aa6fb9b3a981 100755 (executable)
@@ -166,19 +166,31 @@ test_expect_success 'checkout -m with merge conflict' '
        ! test -s current
 '
 
-test_expect_success 'checkout to detach HEAD' '
+test_expect_success 'checkout to detach HEAD (with advice declined)' '
 
+       git config advice.detachedHead false &&
        git checkout -f renamer && git clean -f &&
        git checkout renamer^ 2>messages &&
-       (cat >messages.expect <<EOF
-Note: moving to '\''renamer^'\'' which isn'\''t a local branch
-If you want to create a new branch from this checkout, you may do so
-(now or later) by using -b with the checkout command again. Example:
-  git checkout -b <new_branch_name>
-HEAD is now at 7329388... Initial A one, A two
-EOF
-) &&
-       test_cmp messages.expect messages &&
+       grep "HEAD is now at 7329388" messages &&
+       test 1 -eq $(wc -l <messages) &&
+       H=$(git rev-parse --verify HEAD) &&
+       M=$(git show-ref -s --verify refs/heads/master) &&
+       test "z$H" = "z$M" &&
+       if git symbolic-ref HEAD >/dev/null 2>&1
+       then
+               echo "OOPS, HEAD is still symbolic???"
+               false
+       else
+               : happy
+       fi
+'
+
+test_expect_success 'checkout to detach HEAD' '
+       git config advice.detachedHead true &&
+       git checkout -f renamer && git clean -f &&
+       git checkout renamer^ 2>messages &&
+       grep "HEAD is now at 7329388" messages &&
+       test 1 -lt $(wc -l <messages) &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
        test "z$H" = "z$M" &&
index 8e2449d24409bab14558f83617f651c3f7255627..1382a8e58a14c8fcc92889aad492d5883fd64f2b 100755 (executable)
@@ -28,6 +28,8 @@ test_expect_success 'setup a submodule tree' '
        git commit -m upstream
        git clone . super &&
        git clone super submodule &&
+       git clone super rebasing &&
+       git clone super merging &&
        (cd super &&
         git submodule add ../submodule submodule &&
         test_tick &&
@@ -45,6 +47,16 @@ test_expect_success 'setup a submodule tree' '
         ) &&
         git add submodule &&
         git commit -m "submodule update"
+       ) &&
+       (cd super &&
+        git submodule add ../rebasing rebasing &&
+        test_tick &&
+        git commit -m "rebasing"
+       ) &&
+       (cd super &&
+        git submodule add ../merging merging &&
+        test_tick &&
+        git commit -m "rebasing"
        )
 '
 
@@ -177,21 +189,17 @@ test_expect_success 'submodule update - checkout in .git/config' '
 
 test_expect_success 'submodule init picks up rebase' '
        (cd super &&
-        git config submodule.rebasing.url git://non-existing/git &&
-        git config submodule.rebasing.path does-not-matter &&
-        git config submodule.rebasing.update rebase &&
+        git config -f .gitmodules submodule.rebasing.update rebase &&
         git submodule init rebasing &&
-        test "rebase" = $(git config submodule.rebasing.update)
+        test "rebase" = "$(git config submodule.rebasing.update)"
        )
 '
 
 test_expect_success 'submodule init picks up merge' '
        (cd super &&
-        git config submodule.merging.url git://non-existing/git &&
-        git config submodule.merging.path does-not-matter &&
-        git config submodule.merging.update merge &&
+        git config -f .gitmodules submodule.merging.update merge &&
         git submodule init merging &&
-        test "merge" = $(git config submodule.merging.update)
+        test "merge" = "$(git config submodule.merging.update)"
        )
 '
 
index 2fc7fdb124583f86d5be622510f29ceca1dd3e09..63b6b060e64948771319294251838394264c84a9 100755 (executable)
@@ -591,13 +591,21 @@ test_debug 'cat gitweb.log'
 # ----------------------------------------------------------------------
 # gitweb config and repo config
 
-cat >>gitweb_config.perl <<EOF
-
-\$feature{'blame'}{'override'} = 1;
-\$feature{'snapshot'}{'override'} = 1;
-\$feature{'avatar'}{'override'} = 1;
+cat >>gitweb_config.perl <<\EOF
+
+# turn on override for each overridable feature
+foreach my $key (keys %feature) {
+       if ($feature{$key}{'sub'}) {
+               $feature{$key}{'override'} = 1;
+       }
+}
 EOF
 
+test_expect_success \
+       'config override: projects list (implicit)' \
+       'gitweb_run'
+test_debug 'cat gitweb.log'
+
 test_expect_success \
        'config override: tree view, features not overridden in repo config' \
        'gitweb_run "p=.git;a=tree"'
index afd3053f96b789a73274217b384d245583500c04..a0e396a9522cec96443490fd77ff4abffb335287 100644 (file)
@@ -65,6 +65,8 @@ GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
 # CDPATH into the environment
 unset CDPATH
 
+unset GREP_OPTIONS
+
 case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
        1|2|true)
                echo "* warning: Some tests will not work if GIT_TRACE" \
index 3846aacb476b552cefddd8774b9a055e353b6ebc..08e4fa0354d64e2b931be68ed8da111b97273523 100644 (file)
@@ -918,6 +918,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
        if (!remote)
                die("No remote provided to transport_get()");
 
+       ret->got_remote_refs = 0;
        ret->remote = remote;
        helper = remote->foreign_vcs;
 
@@ -1079,8 +1080,10 @@ int transport_push(struct transport *transport,
 
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
-       if (!transport->remote_refs)
+       if (!transport->got_remote_refs) {
                transport->remote_refs = transport->get_refs_list(transport, 0);
+               transport->got_remote_refs = 1;
+       }
 
        return transport->remote_refs;
 }
index 7cea5cc7234185b1c37ada818dfa1a9114621ad2..6dd9ae182fe12c53860a6af02461ff819d59fd6e 100644 (file)
@@ -19,6 +19,12 @@ struct transport {
        void *data;
        const struct ref *remote_refs;
 
+       /**
+        * Indicates whether we already called get_refs_list(); set by
+        * transport.c::transport_get_remote_refs().
+        */
+       unsigned got_remote_refs : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
index df151813f9c12a681dcac85608f5ff2262c12879..dc464d78b35659705ffb0cd233b80ab27e24e8bc 100644 (file)
@@ -105,12 +105,12 @@ static void show_edge(struct commit *commit)
        fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-static int do_rev_list(int fd, void *create_full_pack)
+static int do_rev_list(int in, int out, void *create_full_pack)
 {
        int i;
        struct rev_info revs;
 
-       pack_pipe = xfdopen(fd, "w");
+       pack_pipe = xfdopen(out, "w");
        init_revisions(&revs, NULL);
        revs.tag_objects = 1;
        revs.tree_objects = 1;
@@ -162,8 +162,9 @@ static void create_pack_file(void)
        int arg = 0;
 
        if (shallow_nr) {
+               memset(&rev_list, 0, sizeof(rev_list));
                rev_list.proc = do_rev_list;
-               rev_list.data = 0;
+               rev_list.out = -1;
                if (start_async(&rev_list))
                        die("git upload-pack: unable to fork git-rev-list");
                argv[arg++] = "pack-objects";
diff --git a/utf8.c b/utf8.c
index ab326ac83e0d9e81b06abff58b00a98341adcd36..84cfc72e6db144880febad0a4ffa64800919fb6d 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -280,22 +280,11 @@ int is_utf8(const char *text)
        return 1;
 }
 
-static inline void strbuf_write(struct strbuf *sb, const char *buf, int len)
+static void strbuf_addchars(struct strbuf *sb, int c, size_t n)
 {
-       if (sb)
-               strbuf_insert(sb, sb->len, buf, len);
-       else
-               fwrite(buf, len, 1, stdout);
-}
-
-static void print_spaces(struct strbuf *buf, int count)
-{
-       static const char s[] = "                    ";
-       while (count >= sizeof(s)) {
-               strbuf_write(buf, s, sizeof(s) - 1);
-               count -= sizeof(s) - 1;
-       }
-       strbuf_write(buf, s, count);
+       strbuf_grow(sb, n);
+       memset(sb->buf + sb->len, c, n);
+       strbuf_setlen(sb, sb->len + n);
 }
 
 static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
@@ -307,8 +296,8 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
                const char *eol = strchrnul(text, '\n');
                if (*eol == '\n')
                        eol++;
-               print_spaces(buf, indent);
-               strbuf_write(buf, text, eol - text);
+               strbuf_addchars(buf, ' ', indent);
+               strbuf_add(buf, text, eol - text);
                text = eol;
                indent = indent2;
        }
@@ -335,16 +324,21 @@ static size_t display_mode_esc_sequence_len(const char *s)
  * consumed (and no extra indent is necessary for the first line).
  */
 int strbuf_add_wrapped_text(struct strbuf *buf,
-               const char *text, int indent, int indent2, int width)
+               const char *text, int indent1, int indent2, int width)
 {
-       int w = indent, assume_utf8 = is_utf8(text);
-       const char *bol = text, *space = NULL;
+       int indent, w, assume_utf8 = 1;
+       const char *bol, *space, *start = text;
+       size_t orig_len = buf->len;
 
        if (width <= 0) {
-               strbuf_add_indented_text(buf, text, indent, indent2);
+               strbuf_add_indented_text(buf, text, indent1, indent2);
                return 1;
        }
 
+retry:
+       bol = text;
+       w = indent = indent1;
+       space = NULL;
        if (indent < 0) {
                w = -indent;
                space = text;
@@ -366,8 +360,8 @@ int strbuf_add_wrapped_text(struct strbuf *buf,
                                if (space)
                                        start = space;
                                else
-                                       print_spaces(buf, indent);
-                               strbuf_write(buf, start, text - start);
+                                       strbuf_addchars(buf, ' ', indent);
+                               strbuf_add(buf, start, text - start);
                                if (!c)
                                        return w;
                                space = text;
@@ -376,40 +370,41 @@ int strbuf_add_wrapped_text(struct strbuf *buf,
                                else if (c == '\n') {
                                        space++;
                                        if (*space == '\n') {
-                                               strbuf_write(buf, "\n", 1);
+                                               strbuf_addch(buf, '\n');
                                                goto new_line;
                                        }
                                        else if (!isalnum(*space))
                                                goto new_line;
                                        else
-                                               strbuf_write(buf, " ", 1);
+                                               strbuf_addch(buf, ' ');
                                }
                                w++;
                                text++;
                        }
                        else {
 new_line:
-                               strbuf_write(buf, "\n", 1);
+                               strbuf_addch(buf, '\n');
                                text = bol = space + isspace(*space);
                                space = NULL;
                                w = indent = indent2;
                        }
                        continue;
                }
-               if (assume_utf8)
+               if (assume_utf8) {
                        w += utf8_width(&text, NULL);
-               else {
+                       if (!text) {
+                               assume_utf8 = 0;
+                               text = start;
+                               strbuf_setlen(buf, orig_len);
+                               goto retry;
+                       }
+               } else {
                        w++;
                        text++;
                }
        }
 }
 
-int print_wrapped_text(const char *text, int indent, int indent2, int width)
-{
-       return strbuf_add_wrapped_text(NULL, text, indent, indent2, width);
-}
-
 int is_encoding_utf8(const char *name)
 {
        if (!name)
diff --git a/utf8.h b/utf8.h
index c9738d83d991d89bbdd4c5a6442fcad2fdaaa4df..ebc4d2fa85113c971ab4c4eaa52537a688a03745 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -8,7 +8,6 @@ int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 
-int print_wrapped_text(const char *text, int indent, int indent2, int len);
 int strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
 
index 0e3e20a3fd38f6f99da44483ee0bb9753f2b217a..9c71b21242773f52ca560d7e5e5e52123674d334 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -204,6 +204,16 @@ int xmkstemp(char *template)
        return fd;
 }
 
+int xmkstemp_mode(char *template, int mode)
+{
+       int fd;
+
+       fd = git_mkstemp_mode(template, mode);
+       if (fd < 0)
+               die_errno("Unable to create temporary file");
+       return fd;
+}
+
 /*
  * zlib wrappers to make sure we don't silently miss errors
  * at init time.
@@ -267,10 +277,14 @@ int git_inflate(z_streamp strm, int flush)
 int odb_mkstemp(char *template, size_t limit, const char *pattern)
 {
        int fd;
-
+       /*
+        * we let the umask do its job, don't try to be more
+        * restrictive except to remove write permission.
+        */
+       int mode = 0444;
        snprintf(template, limit, "%s/%s",
                 get_object_directory(), pattern);
-       fd = mkstemp(template);
+       fd = git_mkstemp_mode(template, mode);
        if (0 <= fd)
                return fd;
 
@@ -279,7 +293,7 @@ int odb_mkstemp(char *template, size_t limit, const char *pattern)
        snprintf(template, limit, "%s/%s",
                 get_object_directory(), pattern);
        safe_create_leading_directories(template);
-       return xmkstemp(template);
+       return xmkstemp_mode(template, mode);
 }
 
 int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)