Merge branch 'jc/shortlog-e'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Dec 2007 01:07:03 +0000 (17:07 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Dec 2007 01:07:03 +0000 (17:07 -0800)
* jc/shortlog-e:
shortlog: default to HEAD when the standard input is a tty
Invert numbers and names in the git-shortlog summary mode.
shortlog: document -e option
git-shortlog -e: show e-mail address as well

58 files changed:
Documentation/RelNotes-1.5.4.txt
Documentation/config.txt
Documentation/diff-format.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-apply.txt
Documentation/git-commit.txt
Documentation/git-status.txt
Documentation/gitattributes.txt
Documentation/glossary.txt
Makefile
builtin-apply.c
builtin-blame.c
builtin-commit-tree.c
builtin-commit.c
builtin-fast-export.c
builtin-log.c
builtin-ls-remote.c
builtin-pack-objects.c
builtin-reset.c
builtin-tag.c
cache.h
color.c
config.c
config.mak.in
configure.ac
contrib/emacs/git.el
diff.c
environment.c
git-checkout.sh
git-clone.sh
git-merge-one-file.sh
git-mergetool.sh
git-send-email.perl
git-svn.perl
http-push.c
http.c
http.h
ident.c
merge-recursive.c
pager.c
perl/Makefile.PL
refs.c
setup.c
t/lib-git-svn.sh
t/t4015-diff-whitespace.sh
t/t4019-diff-wserror.sh [new file with mode: 0755]
t/t4124-apply-ws-rule.sh [new file with mode: 0755]
t/t7004-tag.sh
t/t7201-co.sh
t/t7501-commit.sh
t/t7503-pre-commit-hook.sh [new file with mode: 0755]
t/t7504-commit-msg-hook.sh [new file with mode: 0755]
t/t9107-git-svn-migrate.sh
t/t9119-git-svn-info.sh
tree-diff.c
var.c
ws.c [new file with mode: 0644]
index c1ebd6997a1ba4b25d1302f53a9c36aad9959f36..7386e66dc9f80eb7a9cafba3f637f04eaef21e14 100644 (file)
@@ -132,6 +132,10 @@ Updates since v1.5.3
    variable used to mean "do not require -f option to lose untracked
    files", but we now use the safer default).
 
+ * The kinds of whitespace errors "git diff" and "git apply" notice (and
+   fix) can be controlled via 'core.whitespace' configuration variable
+   and 'whitespace' attribute in .gitattributes file.
+
  * "git push" learned --dry-run option to show what would happen if a
    push is run.
 
@@ -200,6 +204,12 @@ Updates since v1.5.3
 
  * "git bisect" learned "skip" action to mark untestable commits.
 
+ * "git bisect visualize" learned a shorter synonym "git bisect view".
+
+ * "git bisect visualize" runs "git log" in a non-windowed
+   environments.  It also can be told what command to run (e.g. "git
+   bisect visualize tig").
+
  * "git format-patch" learned "format.numbered" configuration variable
    to automatically turn --numbered option on when more than one commits
    are formatted.
@@ -220,6 +230,9 @@ Updates since v1.5.3
  * "git checkout" from a subdirectory learned to use "../path" to allow
    checking out a path outside the current directory without cd'ing up.
 
+ * "git checkout" from and to detached HEAD leaves a bit more
+   information in the reflog.
+
  * "git send-email --dry-run" shows full headers for easier diagnosis.
 
  * "git merge-ours" is now built-in.
@@ -233,11 +246,11 @@ Updates since v1.5.3
    descriptive name from From: and Signed-off-by: lines in the commit
    message.
 
- * "git status" from a subdirectory now shows relative paths which makes
-   copy-and-pasting for git-checkout/git-add/git-rm easier.
-
- * "git checkout" from and to detached HEAD leaves a bit more
-   information in the reflog.
+ * "git status" from a subdirectory now shows relative paths, which
+   makes copy-and-pasting for git-checkout/git-add/git-rm easier.  The
+   traditional behaviour to show the full path relative to the top of
+   the work tree can be had by setting status.relativepaths
+   configuration variable to true.
 
  * In addition there are quite a few internal clean-ups. Notably
 
@@ -266,6 +279,6 @@ series.
 
 --
 exec >/var/tmp/1
-O=v1.5.3.7-1111-gd9f4059
+O=v1.5.3.7-1148-gcf7e147
 echo O=`git describe refs/heads/master`
 git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
index 79d51f26ccfa69a3785e6bebcbdce9b320d96e6d..fabe7f859f32aa53ae86244f014e58bd3aa5ee3d 100644 (file)
@@ -295,6 +295,20 @@ core.pager::
        The command that git will use to paginate output.  Can be overridden
        with the `GIT_PAGER` environment variable.
 
+core.whitespace::
+       A comma separated list of common whitespace problems to
+       notice.  `git diff` will use `color.diff.whitespace` to
+       highlight them, and `git apply --whitespace=error` will
+       consider them as errors:
++
+* `trailing-space` treats trailing whitespaces at the end of the line
+  as an error (enabled by default).
+* `space-before-tab` treats a space character that appears immediately
+  before a tab character in the initial indent part of the line as an
+  error (enabled by default).
+* `indent-with-non-tab` treats a line that is indented with 8 or more
+  space characters that can be replaced with tab characters.
+
 alias.*::
        Command aliases for the gitlink:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -387,8 +401,8 @@ color.diff.<slot>::
        which part of the patch to use the specified color, and is one
        of `plain` (context text), `meta` (metainformation), `frag`
        (hunk header), `old` (removed lines), `new` (added lines),
-       `commit` (commit headers), or `whitespace` (highlighting dubious
-       whitespace).  The values of these variables may be specified as
+       `commit` (commit headers), or `whitespace` (highlighting
+       whitespace errors). The values of these variables may be specified as
        in color.branch.<slot>.
 
 color.interactive::
index 2c3a4c433b2a6d2b0846243a4f1dbebeed45236e..400cbb3b1c120b93278472678ee7bdb87a74f95b 100644 (file)
@@ -84,3 +84,64 @@ all parents.
 
 
 include::diff-generate-patch.txt[]
+
+
+other diff formats
+------------------
+
+The `--summary` option describes newly added, deleted, renamed and
+copied files.  The `--stat` option adds diffstat(1) graph to the
+output.  These options can be combined with other options, such as
+`-p`, and are meant for human consumption.
+
+When showing a change that involves a rename or a copy, `--stat` output
+formats the pathnames compactly by combining common prefix and suffix of
+the pathnames.  For example, a change that moves `arch/i386/Makefile` to
+`arch/x86/Makefile` while modifying 4 lines will be shown like this:
+
+------------------------------------
+arch/{i386 => x86}/Makefile    |   4 +--
+------------------------------------
+
+The `--numstat` option gives the diffstat(1) information but is designed
+for easier machine consumption.  An entry in `--numstat` output looks
+like this:
+
+----------------------------------------
+1      2       README
+3      1       arch/{i386 => x86}/Makefile
+----------------------------------------
+
+That is, from left to right:
+
+. the number of added lines;
+. a tab;
+. the number of deleted lines;
+. a tab;
+. pathname (possibly with rename/copy information);
+. a newline.
+
+When `-z` output option is in effect, the output is formatted this way:
+
+----------------------------------------
+1      2       README NUL
+3      1       NUL arch/i386/Makefile NUL arch/x86/Makefile NUL
+----------------------------------------
+
+That is:
+
+. the number of added lines;
+. a tab;
+. the number of deleted lines;
+. a tab;
+. a NUL (only exists if renamed/copied);
+. pathname in preimage;
+. a NUL (only exists if renamed/copied);
+. pathname in postimage (only exists if renamed/copied);
+. a NUL.
+
+The extra `NUL` before the preimage path in renamed case is to allow
+scripts that read the output to tell if the current record being read is
+a single-path record or a rename/copy record without reading ahead.
+After reading added and deleted lines, reading up to `NUL` would yield
+the pathname, but if that is `NUL`, the record will show two paths.
index d0154bbc0a2f868b3f5f1c63dcffd1fc9355ebc3..5d22b7b58c5950fe70d09e0bb7384179ae30220f 100644 (file)
@@ -175,19 +175,19 @@ endif::git-format-patch[]
        Shorthand for "--text".
 
 --ignore-space-at-eol::
-       Ignore changes in white spaces at EOL.
+       Ignore changes in whitespace at EOL.
 
 --ignore-space-change::
-       Ignore changes in amount of white space.  This ignores white
-       space at line end, and consider all other sequences of one or
-       more white space characters to be equivalent.
+       Ignore changes in amount of whitespace.  This ignores whitespace
+       at line end, and considers all other sequences of one or
+       more whitespace characters to be equivalent.
 
 -b::
        Shorthand for "--ignore-space-change".
 
 --ignore-all-space::
-       Ignore white space when comparing lines.  This ignores
-       difference even if one line has white space where the other
+       Ignore whitespace when comparing lines.  This ignores
+       differences even if one line has whitespace where the other
        line has none.
 
 -w::
index bf94cd43bdd8a9e113af37bec5acd568dd1b3930..721ca998c1988de3cb29c43286a2ffe2e516be4c 100644 (file)
@@ -65,7 +65,7 @@ OPTIONS
        operation to a subset of the working tree. See ``Interactive
        mode'' for details.
 
--p, \--patch:
+-p, \--patch::
        Similar to Interactive mode but the initial command loop is
        bypassed and the 'patch' subcommand is invoked using each of
        the specified filepatterns before exiting.
index c1c54bfe0b7d2c1b133e245a3a963caa0b7afb8c..9ec38f92ba2f9ac51b5cd6ade69758ded31194d6 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
          [--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
-         [--whitespace=<nowarn|warn|error|error-all|strip>]
+         [--whitespace=<nowarn|warn|fix|error|error-all>]
          [--exclude=PATH] [--verbose] [<patch>...]
 
 DESCRIPTION
@@ -119,7 +119,7 @@ discouraged.
 
 --no-add::
        When applying a patch, ignore additions made by the
-       patch.  This can be used to extract common part between
+       patch.  This can be used to extract the common part between
        two files by first running `diff` on them and applying
        the result with this option, which would apply the
        deletion part but not addition part.
@@ -135,25 +135,32 @@ discouraged.
        be useful when importing patchsets, where you want to exclude certain
        files or directories.
 
---whitespace=<option>::
-       When applying a patch, detect a new or modified line
-       that ends with trailing whitespaces (this includes a
-       line that solely consists of whitespaces).  By default,
-       the command outputs warning messages and applies the
-       patch.
-       When gitlink:git-apply[1] is used for statistics and not applying a
-       patch, it defaults to `nowarn`.
-       You can use different `<option>` to control this
-       behavior:
+--whitespace=<action>::
+       When applying a patch, detect a new or modified line that has
+       whitespace errors.  What are considered whitespace errors is
+       controlled by `core.whitespace` configuration.  By default,
+       trailing whitespaces (including lines that solely consist of
+       whitespaces) and a space character that is immediately followed
+       by a tab character inside the initial indent of the line are
+       considered whitespace errors.
++
+By default, the command outputs warning messages but applies the patch.
+When gitlink:git-apply[1] is used for statistics and not applying a
+patch, it defaults to `nowarn`.
++
+You can use different `<action>` to control this
+behavior:
 +
 * `nowarn` turns off the trailing whitespace warning.
 * `warn` outputs warnings for a few such errors, but applies the
-  patch (default).
+  patch as-is (default).
+* `fix` outputs warnings for a few such errors, and applies the
+  patch after fixing them (`strip` is a synonym --- the tool
+  used to consider only trailing whitespaces as errors, and the
+  fix involved 'stripping' them, but modern gits do more).
 * `error` outputs warnings for a few such errors, and refuses
   to apply the patch.
 * `error-all` is similar to `error` but shows all errors.
-* `strip` outputs warnings for a few such errors, strips out the
-  trailing whitespaces and applies the patch.
 
 --inaccurate-eof::
        Under certain circumstances, some versions of diff do not correctly
index 4bb279155053c5ec8f31b01a04803a992ada24f0..4261384158f53ebe85dc32148b5652dc697e5b3b 100644 (file)
@@ -86,7 +86,7 @@ OPTIONS
        Add Signed-off-by line at the end of the commit message.
 
 --no-verify::
-       This option bypasses the pre-commit hook.
+       This option bypasses the pre-commit and commit-msg hooks.
        See also link:hooks.html[hooks].
 
 --allow-empty::
index a1bb9bd8290777c41c99b1bfc3a0e6f111705a6d..5c5a480ec45d181a548a39c8a88dba3e3692eaa8 100644 (file)
@@ -28,13 +28,6 @@ If there is no path that is different between the index file and
 the current HEAD commit (i.e., there is nothing to commit by running
 `git-commit`), the command exits with non-zero status.
 
-If any paths have been touched in the working tree (that is,
-their modification times have changed) but their contents and
-permissions are identical to those in the index file, the command
-updates the index file. Running `git-status` can thus speed up
-subsequent operations such as `git-diff` if the working tree
-contains many paths that have been touched but not modified.
-
 
 OUTPUT
 ------
index 19bd25f29900c99d78592fe290772856092e2dc5..71c7ad76d57f5ce89206ccbc8f4c66cadd8f7743 100644 (file)
@@ -361,6 +361,37 @@ When left unspecified, the driver itself is used for both
 internal merge and the final merge.
 
 
+Checking whitespace errors
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`whitespace`
+^^^^^^^^^^^^
+
+The `core.whitespace` configuration variable allows you to define what
+`diff` and `apply` should consider whitespace errors for all paths in
+the project (See gitlink:git-config[1]).  This attribute gives you finer
+control per path.
+
+Set::
+
+       Notice all types of potential whitespace errors known to git.
+
+Unset::
+
+       Do not notice anything as error.
+
+Unspecified::
+
+       Use the value of `core.whitespace` configuration variable to
+       decide what to notice as error.
+
+String::
+
+       Specify a comma separate list of common whitespace problems to
+       notice in the same format as `core.whitespace` configuration
+       variable.
+
+
 EXAMPLE
 -------
 
index fc1874424e26a2f95574d72bf3fc1c71a3b1a1b6..65f55e4ced20b7a957b83be3d47764dcfe3238c5 100644 (file)
@@ -244,8 +244,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
        of the object's contents using the Secure Hash Algorithm
        1 and usually represented by the 40 character hexadecimal encoding of
-       the <<def_hash,hash>> of the object (possibly followed by
-       a white space).
+       the <<def_hash,hash>> of the object.
 
 [[def_object_type]]object type::
        One of the identifiers
index 3a119ae7b0b0f39fb72f3b70fa393a133bdb88fc..3615be0b730727d557589f7a1c3117beab6ad77a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -313,7 +313,7 @@ LIB_OBJS = \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
-       transport.o bundle.o walker.o parse-options.o
+       transport.o bundle.o walker.o parse-options.o ws.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
@@ -1025,7 +1025,7 @@ install: all
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
-       $(MAKE) -C perl prefix='$(prefix_SQ)' install
+       $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_TCLTK
        $(MAKE) -C gitk-git install
        $(MAKE) -C git-gui install
index 91f8752ff7fdb588e64f594519a2e9503c516630..f2e9a332ca0e148366f49b2825ed8826576f1bad 100644 (file)
@@ -45,14 +45,14 @@ static const char *fake_ancestor;
 static int line_termination = '\n';
 static unsigned long p_context = ULONG_MAX;
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
-
-static enum whitespace_eol {
-       nowarn_whitespace,
-       warn_on_whitespace,
-       error_on_whitespace,
-       strip_whitespace,
-} new_whitespace = warn_on_whitespace;
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...";
+
+static enum ws_error_action {
+       nowarn_ws_error,
+       warn_on_ws_error,
+       die_on_ws_error,
+       correct_ws_error,
+} ws_error_action = warn_on_ws_error;
 static int whitespace_error;
 static int squelch_whitespace_errors = 5;
 static int applied_after_fixing_ws;
@@ -61,28 +61,28 @@ static const char *patch_input_file;
 static void parse_whitespace_option(const char *option)
 {
        if (!option) {
-               new_whitespace = warn_on_whitespace;
+               ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "warn")) {
-               new_whitespace = warn_on_whitespace;
+               ws_error_action = warn_on_ws_error;
                return;
        }
        if (!strcmp(option, "nowarn")) {
-               new_whitespace = nowarn_whitespace;
+               ws_error_action = nowarn_ws_error;
                return;
        }
        if (!strcmp(option, "error")) {
-               new_whitespace = error_on_whitespace;
+               ws_error_action = die_on_ws_error;
                return;
        }
        if (!strcmp(option, "error-all")) {
-               new_whitespace = error_on_whitespace;
+               ws_error_action = die_on_ws_error;
                squelch_whitespace_errors = 0;
                return;
        }
-       if (!strcmp(option, "strip")) {
-               new_whitespace = strip_whitespace;
+       if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
+               ws_error_action = correct_ws_error;
                return;
        }
        die("unrecognized whitespace option '%s'", option);
@@ -90,11 +90,8 @@ static void parse_whitespace_option(const char *option)
 
 static void set_default_whitespace_mode(const char *whitespace_option)
 {
-       if (!whitespace_option && !apply_default_whitespace) {
-               new_whitespace = (apply
-                                 ? warn_on_whitespace
-                                 : nowarn_whitespace);
-       }
+       if (!whitespace_option && !apply_default_whitespace)
+               ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
 }
 
 /*
@@ -137,11 +134,17 @@ struct fragment {
 #define BINARY_DELTA_DEFLATED  1
 #define BINARY_LITERAL_DEFLATED 2
 
+/*
+ * This represents a "patch" to a file, both metainfo changes
+ * such as creation/deletion, filemode and content changes represented
+ * as a series of fragments.
+ */
 struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
        int is_new, is_delete;  /* -1 = unknown, 0 = false, 1 = true */
        int rejected;
+       unsigned ws_rule;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
@@ -158,7 +161,8 @@ struct patch {
        struct patch *next;
 };
 
-static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+static void say_patch_name(FILE *output, const char *pre,
+                          struct patch *patch, const char *post)
 {
        fputs(pre, output);
        if (patch->old_name && patch->new_name &&
@@ -229,7 +233,8 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
        if (*line == '"') {
                struct strbuf name;
 
-               /* Proposed "new-style" GNU patch/diff format; see
+               /*
+                * Proposed "new-style" GNU patch/diff format; see
                 * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
                 */
                strbuf_init(&name, 0);
@@ -499,7 +504,8 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
 
 static int gitdiff_index(const char *line, struct patch *patch)
 {
-       /* index line is N hexadecimal, "..", N hexadecimal,
+       /*
+        * index line is N hexadecimal, "..", N hexadecimal,
         * and optional space with octal mode.
         */
        const char *ptr, *eol;
@@ -550,7 +556,8 @@ static const char *stop_at_slash(const char *line, int llen)
        return NULL;
 }
 
-/* This is to extract the same name that appears on "diff --git"
+/*
+ * This is to extract the same name that appears on "diff --git"
  * line.  We do not find and return anything if it is a rename
  * patch, and it is OK because we will find the name elsewhere.
  * We need to reliably find name only when it is mode-change only,
@@ -584,7 +591,8 @@ static char *git_header_name(char *line, int llen)
                        goto free_and_fail1;
                strbuf_remove(&first, 0, cp + 1 - first.buf);
 
-               /* second points at one past closing dq of name.
+               /*
+                * second points at one past closing dq of name.
                 * find the second name.
                 */
                while ((second < line + llen) && isspace(*second))
@@ -627,7 +635,8 @@ static char *git_header_name(char *line, int llen)
                return NULL;
        name++;
 
-       /* since the first name is unquoted, a dq if exists must be
+       /*
+        * since the first name is unquoted, a dq if exists must be
         * the beginning of the second name.
         */
        for (second = name; second < line + llen; second++) {
@@ -758,7 +767,7 @@ static int parse_num(const char *line, unsigned long *p)
 }
 
 static int parse_range(const char *line, int len, int offset, const char *expect,
-                       unsigned long *p1, unsigned long *p2)
+                      unsigned long *p1, unsigned long *p2)
 {
        int digits, ex;
 
@@ -867,14 +876,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                        return offset;
                }
 
-               /** --- followed by +++ ? */
+               /* --- followed by +++ ? */
                if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
                        continue;
 
                /*
                 * We only accept unified patches, so we want it to
                 * at least have "@@ -a,b +c,d @@\n", which is 14 chars
-                * minimum
+                * minimum ("@@ -0,0 +1 @@\n" is the shortest).
                 */
                nextlen = linelen(line + len, size - len);
                if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
@@ -889,7 +898,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
        return -1;
 }
 
-static void check_whitespace(const char *line, int len)
+static void check_whitespace(const char *line, int len, unsigned ws_rule)
 {
        const char *err = "Adds trailing whitespace";
        int seen_space = 0;
@@ -901,23 +910,35 @@ static void check_whitespace(const char *line, int len)
         * this function.  That is, an addition of an empty line would
         * check the '+' here.  Sneaky...
         */
-       if (isspace(line[len-2]))
+       if ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))
                goto error;
 
        /*
         * Make sure that there is no space followed by a tab in
         * indentation.
         */
-       err = "Space in indent is followed by a tab";
-       for (i = 1; i < len; i++) {
-               if (line[i] == '\t') {
-                       if (seen_space)
-                               goto error;
-               }
-               else if (line[i] == ' ')
-                       seen_space = 1;
-               else
-                       break;
+       if (ws_rule & WS_SPACE_BEFORE_TAB) {
+               err = "Space in indent is followed by a tab";
+               for (i = 1; i < len; i++) {
+                       if (line[i] == '\t') {
+                               if (seen_space)
+                                       goto error;
+                       }
+                       else if (line[i] == ' ')
+                               seen_space = 1;
+                       else
+                               break;
+               }
+       }
+
+       /*
+        * Make sure that the indentation does not contain more than
+        * 8 spaces.
+        */
+       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+           (8 < len) && !strncmp("+        ", line, 9)) {
+               err = "Indent more than 8 places with spaces";
+               goto error;
        }
        return;
 
@@ -931,14 +952,14 @@ static void check_whitespace(const char *line, int len)
                        err, patch_input_file, linenr, len-2, line+1);
 }
 
-
 /*
  * Parse a unified diff. Note that this really needs to parse each
  * fragment separately, since the only way to know the difference
  * between a "---" that is part of a patch, and a "---" that starts
  * the next patch is to look at the line counts..
  */
-static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
+static int parse_fragment(char *line, unsigned long size,
+                         struct patch *patch, struct fragment *fragment)
 {
        int added, deleted;
        int len = linelen(line, size), offset;
@@ -979,22 +1000,23 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                        break;
                case '-':
                        if (apply_in_reverse &&
-                                       new_whitespace != nowarn_whitespace)
-                               check_whitespace(line, len);
+                           ws_error_action != nowarn_ws_error)
+                               check_whitespace(line, len, patch->ws_rule);
                        deleted++;
                        oldlines--;
                        trailing = 0;
                        break;
                case '+':
                        if (!apply_in_reverse &&
-                                       new_whitespace != nowarn_whitespace)
-                               check_whitespace(line, len);
+                           ws_error_action != nowarn_ws_error)
+                               check_whitespace(line, len, patch->ws_rule);
                        added++;
                        newlines--;
                        trailing = 0;
                        break;
 
-                /* We allow "\ No newline at end of file". Depending
+               /*
+                * We allow "\ No newline at end of file". Depending
                  * on locale settings when the patch was produced we
                  * don't know what this line looks like. The only
                  * thing we do know is that it begins with "\ ".
@@ -1012,7 +1034,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
        fragment->leading = leading;
        fragment->trailing = trailing;
 
-       /* If a fragment ends with an incomplete line, we failed to include
+       /*
+        * If a fragment ends with an incomplete line, we failed to include
         * it in the above loop because we hit oldlines == newlines == 0
         * before seeing it.
         */
@@ -1140,7 +1163,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
                                          int *status_p,
                                          int *used_p)
 {
-       /* Expect a line that begins with binary patch method ("literal"
+       /*
+        * Expect a line that begins with binary patch method ("literal"
         * or "delta"), followed by the length of data before deflating.
         * a sequence of 'length-byte' followed by base-85 encoded data
         * should follow, terminated by a newline.
@@ -1189,7 +1213,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
                        size--;
                        break;
                }
-               /* Minimum line is "A00000\n" which is 7-byte long,
+               /*
+                * Minimum line is "A00000\n" which is 7-byte long,
                 * and the line length must be multiple of 5 plus 2.
                 */
                if ((llen < 7) || (llen-2) % 5)
@@ -1240,7 +1265,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
 
 static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
 {
-       /* We have read "GIT binary patch\n"; what follows is a line
+       /*
+        * We have read "GIT binary patch\n"; what follows is a line
         * that says the patch method (currently, either "literal" or
         * "delta") and the length of data before deflating; a
         * sequence of 'length-byte' followed by base-85 encoded data
@@ -1270,7 +1296,8 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        if (reverse)
                used += used_1;
        else if (status) {
-               /* not having reverse hunk is not an error, but having
+               /*
+                * Not having reverse hunk is not an error, but having
                 * a corrupt reverse hunk is.
                 */
                free((void*) forward->patch);
@@ -1291,7 +1318,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
        if (offset < 0)
                return offset;
 
-       patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
+       patch->ws_rule = whitespace_rule(patch->new_name
+                                        ? patch->new_name
+                                        : patch->old_name);
+
+       patchsize = parse_single_patch(buffer + offset + hdrsize,
+                                      size - offset - hdrsize, patch);
 
        if (!patchsize) {
                static const char *binhdr[] = {
@@ -1367,8 +1399,10 @@ static void reverse_patches(struct patch *p)
        }
 }
 
-static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]= "----------------------------------------------------------------------";
+static const char pluses[] =
+"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]=
+"----------------------------------------------------------------------";
 
 static void show_stats(struct patch *patch)
 {
@@ -1437,7 +1471,9 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        }
 }
 
-static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
+static int find_offset(const char *buf, unsigned long size,
+                      const char *fragment, unsigned long fragsize,
+                      int line, int *lines)
 {
        int i;
        unsigned long start, backwards, forwards;
@@ -1536,9 +1572,11 @@ static void remove_last_line(const char **rbuf, int *rsize)
        *rsize = offset + 1;
 }
 
-static int apply_line(char *output, const char *patch, int plen)
+static int apply_line(char *output, const char *patch, int plen,
+                     unsigned ws_rule)
 {
-       /* plen is number of bytes to be copied from patch,
+       /*
+        * plen is number of bytes to be copied from patch,
         * starting at patch+1 (patch[0] is '+').  Typically
         * patch[plen] is '\n', unless this is the incomplete
         * last line.
@@ -1551,13 +1589,17 @@ static int apply_line(char *output, const char *patch, int plen)
        int need_fix_leading_space = 0;
        char *buf;
 
-       if ((new_whitespace != strip_whitespace) || !whitespace_error ||
+       if ((ws_error_action != correct_ws_error) || !whitespace_error ||
            *patch != '+') {
                memcpy(output, patch + 1, plen);
                return plen;
        }
 
-       if (1 < plen && isspace(patch[plen-1])) {
+       /*
+        * Strip trailing whitespace
+        */
+       if ((ws_rule & WS_TRAILING_SPACE) &&
+           (1 < plen && isspace(patch[plen-1]))) {
                if (patch[plen] == '\n')
                        add_nl_to_tail = 1;
                plen--;
@@ -1566,15 +1608,23 @@ static int apply_line(char *output, const char *patch, int plen)
                fixed = 1;
        }
 
+       /*
+        * Check leading whitespaces (indent)
+        */
        for (i = 1; i < plen; i++) {
                char ch = patch[i];
                if (ch == '\t') {
                        last_tab_in_indent = i;
-                       if (0 <= last_space_in_indent)
+                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+                           0 <= last_space_in_indent)
+                           need_fix_leading_space = 1;
+               } else if (ch == ' ') {
+                       last_space_in_indent = i;
+                       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+                           last_tab_in_indent < 0 &&
+                           8 <= i)
                                need_fix_leading_space = 1;
                }
-               else if (ch == ' ')
-                       last_space_in_indent = i;
                else
                        break;
        }
@@ -1582,10 +1632,21 @@ static int apply_line(char *output, const char *patch, int plen)
        buf = output;
        if (need_fix_leading_space) {
                int consecutive_spaces = 0;
-               /* between patch[1..last_tab_in_indent] strip the
-                * funny spaces, updating them to tab as needed.
+               int last = last_tab_in_indent + 1;
+
+               if (ws_rule & WS_INDENT_WITH_NON_TAB) {
+                       /* have "last" point at one past the indent */
+                       if (last_tab_in_indent < last_space_in_indent)
+                               last = last_space_in_indent + 1;
+                       else
+                               last = last_tab_in_indent + 1;
+               }
+
+               /*
+                * between patch[1..last], strip the funny spaces,
+                * updating them to tab as needed.
                 */
-               for (i = 1; i < last_tab_in_indent; i++, plen--) {
+               for (i = 1; i < last; i++, plen--) {
                        char ch = patch[i];
                        if (ch != ' ') {
                                consecutive_spaces = 0;
@@ -1598,8 +1659,10 @@ static int apply_line(char *output, const char *patch, int plen)
                                }
                        }
                }
+               while (0 < consecutive_spaces--)
+                       *output++ = ' ';
                fixed = 1;
-               i = last_tab_in_indent;
+               i = last;
        }
        else
                i = 1;
@@ -1612,7 +1675,8 @@ static int apply_line(char *output, const char *patch, int plen)
        return output + plen - buf;
 }
 
-static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
+static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
+                             int inaccurate_eof, unsigned ws_rule)
 {
        int match_beginning, match_end;
        const char *patch = frag->patch;
@@ -1671,7 +1735,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
                case '+':
                        if (first != '+' || !no_add) {
                                int added = apply_line(new + newsize, patch,
-                                                      plen);
+                                                      plen, ws_rule);
                                newsize += added;
                                if (first == '+' &&
                                    added == 1 && new[newsize-1] == '\n')
@@ -1694,8 +1758,9 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
                size -= len;
        }
 
-       if (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\n' &&
-                       newsize > 0 && new[newsize - 1] == '\n') {
+       if (inaccurate_eof &&
+           oldsize > 0 && old[oldsize - 1] == '\n' &&
+           newsize > 0 && new[newsize - 1] == '\n') {
                oldsize--;
                newsize--;
        }
@@ -1732,7 +1797,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
                if (match_beginning && offset)
                        offset = -1;
                if (offset >= 0) {
-                       if (new_whitespace == strip_whitespace &&
+                       if (ws_error_action == correct_ws_error &&
                            (buf->len - oldsize - offset == 0)) /* end of file? */
                                newsize -= new_blank_lines_at_end;
 
@@ -1757,9 +1822,10 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
                        match_beginning = match_end = 0;
                        continue;
                }
-               /* Reduce the number of context lines
-                * Reduce both leading and trailing if they are equal
-                * otherwise just reduce the larger context.
+               /*
+                * Reduce the number of context lines; reduce both
+                * leading and trailing if they are equal otherwise
+                * just reduce the larger context.
                 */
                if (leading >= trailing) {
                        remove_first_line(&oldlines, &oldsize);
@@ -1819,7 +1885,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
 
-       /* For safety, we require patch index line to contain
+       /*
+        * For safety, we require patch index line to contain
         * full 40-byte textual SHA1 for old and new, at least for now.
         */
        if (strlen(patch->old_sha1_prefix) != 40 ||
@@ -1830,7 +1897,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                             "without full index line", name);
 
        if (patch->old_name) {
-               /* See if the old one matches what the patch
+               /*
+                * See if the old one matches what the patch
                 * applies to.
                 */
                hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
@@ -1867,7 +1935,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
                /* XXX read_sha1_file NUL-terminates */
                strbuf_attach(buf, result, size, size + 1);
        } else {
-               /* We have verified buf matches the preimage;
+               /*
+                * We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
@@ -1889,12 +1958,14 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
 {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
+       unsigned ws_rule = patch->ws_rule;
+       unsigned inaccurate_eof = patch->inaccurate_eof;
 
        if (patch->is_binary)
                return apply_binary(buf, patch);
 
        while (frag) {
-               if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
+               if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@ -2066,7 +2137,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 
        if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
-               /* A type-change diff is always split into a patch to
+               /*
+                * A type-change diff is always split into a patch to
                 * delete old, immediately followed by a patch to
                 * create new (see diff.c::run_diff()); in such a case
                 * it is Ok that the entry to be deleted by the
@@ -2670,7 +2742,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
                offset += nr;
        }
 
-       if (whitespace_error && (new_whitespace == error_on_whitespace))
+       if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
 
        update_index = check_index && apply;
@@ -2865,7 +2937,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                                squelched,
                                squelched == 1 ? "" : "s");
                }
-               if (new_whitespace == error_on_whitespace)
+               if (ws_error_action == die_on_ws_error)
                        die("%d line%s add%s whitespace errors.",
                            whitespace_error,
                            whitespace_error == 1 ? "" : "s",
index c158d319dce78c6b1f0cfeffd91ee565a2d14eff..5466d01f9a5917dbba4108669ee4513b581ff2c9 100644 (file)
@@ -130,6 +130,14 @@ static void origin_decref(struct origin *o)
        }
 }
 
+static void drop_origin_blob(struct origin *o)
+{
+       if (o->file.ptr) {
+               free(o->file.ptr);
+               o->file.ptr = NULL;
+       }
+}
+
 /*
  * Each group of lines is described by a blame_entry; it can be split
  * as we pass blame to the parents.  They form a linked list in the
@@ -380,6 +388,7 @@ static struct origin *find_origin(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
+       diff_tree_release_paths(&diff_opts);
        if (porigin) {
                /*
                 * Create a freestanding copy that is not part of
@@ -436,6 +445,7 @@ static struct origin *find_rename(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
+       diff_tree_release_paths(&diff_opts);
        return porigin;
 }
 
@@ -1157,7 +1167,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
                }
        }
        diff_flush(&diff_opts);
-
+       diff_tree_release_paths(&diff_opts);
        return retval;
 }
 
@@ -1274,8 +1284,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
                }
 
  finish:
-       for (i = 0; i < MAXPARENT; i++)
-               origin_decref(parent_origin[i]);
+       for (i = 0; i < MAXPARENT; i++) {
+               if (parent_origin[i]) {
+                       drop_origin_blob(parent_origin[i]);
+                       origin_decref(parent_origin[i]);
+               }
+       }
+       drop_origin_blob(origin);
 }
 
 /*
index 88b0ab36eba6ded8f1a39e0d4c83122b7e026874..6610d18358bae81ac1f162ca0c71062d36664c2a 100644 (file)
@@ -98,8 +98,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
 
        /* Person/date information */
-       strbuf_addf(&buffer, "author %s\n", git_author_info(1));
-       strbuf_addf(&buffer, "committer %s\n", git_committer_info(1));
+       strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME));
+       strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
        if (!encoding_is_utf8)
                strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
        strbuf_addch(&buffer, '\n');
index 19297ac027ab88ffe01d4b9b5dc12c7ae556f6c1..9cb7589ac68172591ae458a20031e7dedd65adee 100644 (file)
@@ -497,7 +497,7 @@ static void determine_author_info(struct strbuf *sb)
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
 
-       strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
+       strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME));
 }
 
 static int parse_and_validate_options(int argc, const char *argv[],
@@ -660,12 +660,16 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
        rev.commit_format = get_commit_format("format:%h: %s");
-       rev.always_show_header = 1;
+       rev.always_show_header = 0;
 
        printf("Created %scommit ", initial_commit ? "initial " : "");
 
-       log_tree_commit(&rev, commit);
-       printf("\n");
+       if (!log_tree_commit(&rev, commit)) {
+               struct strbuf buf = STRBUF_INIT;
+               format_commit_message(commit, "%h: %s", &buf);
+               printf("%s\n", buf.buf);
+               strbuf_release(&buf);
+       }
 }
 
 int git_commit_config(const char *k, const char *v)
@@ -776,7 +780,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        determine_author_info(&sb);
-       strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
+       strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
        if (!is_encoding_utf8(git_commit_encoding))
                strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
        strbuf_addch(&sb, '\n');
@@ -787,15 +791,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                char index[PATH_MAX];
                const char *env[2] = { index, NULL };
                snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-               launch_editor(git_path(commit_editmsg), &sb, env);
-       } else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
-               rollback_index_files();
-               die("could not read commit message");
+               launch_editor(git_path(commit_editmsg), NULL, env);
        }
-       if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
+       if (!no_verify &&
+           run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
                rollback_index_files();
                exit(1);
        }
+       if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+               rollback_index_files();
+               die("could not read commit message");
+       }
 
        /* Truncate the message just before the diff, if any. */
        p = strstr(sb.buf, "\ndiff --git a/");
index 2136aadfd7831828ee29fbfa9ae7974c65271dee..ef27eee71bdef2cac6f751e07b1da71588989b03 100755 (executable)
@@ -103,7 +103,7 @@ static void handle_object(const unsigned char *sha1)
        mark_object(object);
 
        printf("blob\nmark :%d\ndata %lu\n", last_idnum, size);
-       if (fwrite(buf, size, 1, stdout) != 1)
+       if (size && fwrite(buf, size, 1, stdout) != 1)
                die ("Could not write blob %s", sha1_to_hex(sha1));
        printf("\n");
 
index d375c9dbf9759d9ddc8a0029bc5803057e5f117d..cc3cc9069a824740d2d5cceba95597e036f67120 100644 (file)
@@ -554,7 +554,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
 
 static void gen_message_id(char *dest, unsigned int length, char *base)
 {
-       const char *committer = git_committer_info(-1);
+       const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
        const char *email_start = strrchr(committer, '<');
        const char *email_end = strrchr(committer, '>');
        if(!email_start || !email_end || email_start > email_end - 1)
@@ -662,7 +662,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                         !strcmp(argv[i], "-s")) {
                        const char *committer;
                        const char *endpos;
-                       committer = git_committer_info(1);
+                       committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        endpos = strchr(committer, '>');
                        if (!endpos)
                                die("bogos committer info %s\n", committer);
index 56f3f880238e79a92ac35b4112e425a05c3fe916..c2caeeabe2f9d2bce190656a58094b5ebeee4e30 100644 (file)
@@ -6,6 +6,27 @@
 static const char ls_remote_usage[] =
 "git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
 
+/*
+ * Is there one among the list of patterns that match the tail part
+ * of the path?
+ */
+static int tail_match(const char **pattern, const char *path)
+{
+       const char *p;
+       char pathbuf[PATH_MAX];
+
+       if (!pattern)
+               return 1; /* no restriction */
+
+       if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
+               return error("insanely long ref %.*s...", 20, path);
+       while ((p = *(pattern++)) != NULL) {
+               if (!fnmatch(p, pathbuf, 0))
+                       return 1;
+       }
+       return 0;
+}
+
 int cmd_ls_remote(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -13,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        int nongit = 0;
        unsigned flags = 0;
        const char *uploadpack = NULL;
+       const char **pattern = NULL;
 
        struct remote *remote;
        struct transport *transport;
@@ -47,12 +69,23 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                        usage(ls_remote_usage);
                }
                dest = arg;
+               i++;
                break;
        }
 
-       if (!dest || i != argc - 1)
+       if (!dest)
                usage(ls_remote_usage);
 
+       if (argv[i]) {
+               int j;
+               pattern = xcalloc(sizeof(const char *), argc - i + 1);
+               for (j = i; j < argc; j++) {
+                       int len = strlen(argv[j]);
+                       char *p = xmalloc(len + 3);
+                       sprintf(p, "*/%s", argv[j]);
+                       pattern[j - i] = p;
+               }
+       }
        remote = nongit ? NULL : remote_get(dest);
        if (remote && !remote->url_nr)
                die("remote %s has no configured URL", dest);
@@ -65,10 +98,12 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        if (!ref)
                return 1;
 
-       while (ref) {
-               if (check_ref_type(ref, flags))
-                       printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
-               ref = ref->next;
+       for ( ; ref; ref = ref->next) {
+               if (!check_ref_type(ref, flags))
+                       continue;
+               if (!tail_match(pattern, ref->name))
+                       continue;
+               printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
        }
        return 0;
 }
index 250dc56ab5121d3ffda916228ed3dbf71d0def88..7dd0d7f82605798a81e731c8e0db1b5c009e1e08 100644 (file)
@@ -1709,6 +1709,16 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                                list++;
                                sub_size--;
                        }
+                       if (!sub_size) {
+                               /*
+                                * It is possible for some "paths" to have
+                                * so many objects that no hash boundary
+                                * might be found.  Let's just steal the
+                                * exact half in that case.
+                                */
+                               sub_size = victim->remaining / 2;
+                               list -= sub_size;
+                       }
                        target->list = list;
                        victim->list_size -= sub_size;
                        victim->remaining -= sub_size;
index 4c61025aaef96ae9f8bcabe3fd4ad6c0fc2beba3..713c2d5346e8dc7e502beffc256bfdcad9da6ad5 100644 (file)
@@ -158,6 +158,7 @@ static int read_from_tree(const char *prefix, const char **argv,
                return 1;
        diffcore_std(&opt);
        diff_flush(&opt);
+       diff_tree_release_paths(&opt);
 
        if (!index_was_discarded)
                /* The index is still clobbered from do_diff_cache() */
index 729389bbd6120e4b395fbc6f916f8d13965191d6..274901a408fa0d1d988da35d637ead7ca565552e 100644 (file)
@@ -53,6 +53,8 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
                        die("There was a problem with the editor %s.", editor);
        }
 
+       if (!buffer)
+               return;
        if (strbuf_read_file(buffer, path, 0) < 0)
                die("could not read message file '%s': %s",
                    path, strerror(errno));
@@ -186,7 +188,7 @@ static int do_sign(struct strbuf *buffer)
        int len;
 
        if (!*signingkey) {
-               if (strlcpy(signingkey, git_committer_info(1),
+               if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
                                sizeof(signingkey)) > sizeof(signingkey) - 1)
                        return error("committer info too long.");
                bracket = strchr(signingkey, '>');
@@ -234,14 +236,18 @@ static const char tag_template[] =
        "# Write a tag message\n"
        "#\n";
 
+static void set_signingkey(const char *value)
+{
+       if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
+               die("signing key value too long (%.10s...)", value);
+}
+
 static int git_tag_config(const char *var, const char *value)
 {
        if (!strcmp(var, "user.signingkey")) {
                if (!value)
                        die("user.signingkey without value");
-               if (strlcpy(signingkey, value, sizeof(signingkey))
-                                               >= sizeof(signingkey))
-                       die("user.signingkey value too long");
+               set_signingkey(value);
                return 0;
        }
 
@@ -296,7 +302,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                          sha1_to_hex(object),
                          typename(type),
                          tag,
-                         git_committer_info(1));
+                         git_committer_info(IDENT_ERROR_ON_NO_NAME));
 
        if (header_len > sizeof(header_buf) - 1)
                die("tag header too big.");
@@ -394,6 +400,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        argc = parse_options(argc, argv, options, git_tag_usage, 0);
 
+       if (keyid) {
+               sign = 1;
+               set_signingkey(keyid);
+       }
        if (sign)
                annotate = 1;
 
diff --git a/cache.h b/cache.h
index 406befb705a0a99119476bbed3be12dd6059d932..27d90fe543b97eac91f61bf96f170c32d735e44d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -453,6 +453,9 @@ void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
 enum date_mode parse_date_format(const char *format);
 
+#define IDENT_WARN_ON_NO_NAME  1
+#define IDENT_ERROR_ON_NO_NAME 2
+#define IDENT_NO_DATE         4
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
@@ -605,7 +608,7 @@ extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char
 /* pager.c */
 extern void setup_pager(void);
 extern char *pager_program;
-extern int pager_in_use;
+extern int pager_in_use(void);
 extern int pager_use_color;
 
 extern char *editor_program;
@@ -641,6 +644,18 @@ extern int diff_auto_refresh_index;
 /* match-trees.c */
 void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 
+/*
+ * whitespace rules.
+ * used by both diff and apply
+ */
+#define WS_TRAILING_SPACE      01
+#define WS_SPACE_BEFORE_TAB    02
+#define WS_INDENT_WITH_NON_TAB 04
+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
+extern unsigned whitespace_rule_cfg;
+extern unsigned whitespace_rule(const char *);
+extern unsigned parse_whitespace_rule(const char *);
+
 /* ls-files */
 int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
diff --git a/color.c b/color.c
index 7bd424a8f6012859f40f2aa6210e7d4ce7686dc0..7f66c29fae57abceda30b1257c2a66626f3be0b2 100644 (file)
--- a/color.c
+++ b/color.c
@@ -135,7 +135,7 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
  auto_color:
        if (stdout_is_tty < 0)
                stdout_is_tty = isatty(1);
-       if (stdout_is_tty || (pager_in_use && pager_use_color)) {
+       if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
                char *term = getenv("TERM");
                if (term && strcmp(term, "dumb"))
                        return 1;
index ed96213c44265289c26d46edaaf740cebd0b4c86..49d2b427e52008e612b534f0c567e9fadaeab6e3 100644 (file)
--- a/config.c
+++ b/config.c
@@ -439,6 +439,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.whitespace")) {
+               whitespace_rule_cfg = parse_whitespace_rule(value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
@@ -646,13 +651,19 @@ static int store_write_pair(int fd, const char* key, const char* value)
        int length = strlen(key+store.baselen+1);
        int quote = 0;
 
-       /* Check to see if the value needs to be quoted. */
+       /*
+        * Check to see if the value needs to be surrounded with a dq pair.
+        * Note that problematic characters are always backslash-quoted; this
+        * check is about not losing leading or trailing SP and strings that
+        * follow beginning-of-comment characters (i.e. ';' and '#') by the
+        * configuration parser.
+        */
        if (value[0] == ' ')
                quote = 1;
        for (i = 0; value[i]; i++)
                if (value[i] == ';' || value[i] == '#')
                        quote = 1;
-       if (value[i-1] == ' ')
+       if (i && value[i-1] == ' ')
                quote = 1;
 
        if (write_in_full(fd, "\t", 1) != 1 ||
index 7d5df9bf3c5dc898a68b538bae55f3d44dbc01ff..15fb26cb9226bd8c65cb6869ef38fe0af0b1260c 100644 (file)
@@ -23,6 +23,7 @@ VPATH = @srcdir@
 export exec_prefix mandir
 export srcdir VPATH
 
+ASCIIDOC8=@ASCIIDOC8@
 NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
 NO_OPENSSL=@NO_OPENSSL@
 NO_CURL=@NO_CURL@
index dd4b4eb9822f131f0efc746013a803c4adba02d2..6f641e32f0d9e802f6fff72cde7388b7a3521769 100644 (file)
@@ -122,6 +122,27 @@ if test -z "$NO_TCLTK"; then
     AC_SUBST(TCLTK_PATH)
   fi
 fi
+AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
+if test -n "$ASCIIDOC"; then
+       AC_MSG_CHECKING([for asciidoc version])
+       asciidoc_version=`$ASCIIDOC --version 2>&1`
+       case "${asciidoc_version}" in
+       asciidoc' '8*)
+               ASCIIDOC8=YesPlease
+               AC_MSG_RESULT([${asciidoc_version} > 7])
+               ;;
+       asciidoc' '7*)
+               ASCIIDOC8=
+               AC_MSG_RESULT([${asciidoc_version}])
+               ;;
+       *)
+               ASCIIDOC8=
+               AC_MSG_RESULT([${asciidoc_version} (unknown)])
+               ;;
+       esac
+fi
+AC_SUBST(ASCIIDOC8)
+
 
 ## Checks for libraries.
 AC_MSG_NOTICE([CHECKS for libraries])
index e147da0596a880e864fb707192b5bc856cb3daba..28a4899a0a46d67978280a1d0657f4b2b98be1d7 100644 (file)
@@ -49,6 +49,7 @@
 (eval-when-compile (require 'cl))
 (require 'ewoc)
 (require 'log-edit)
+(require 'easymenu)
 
 
 ;;;; Customizations
@@ -1297,7 +1298,47 @@ Return the list of files that haven't been handled."
     (define-key toggle-map "i" 'git-toggle-show-ignored)
     (define-key toggle-map "k" 'git-toggle-show-unknown)
     (define-key toggle-map "m" 'git-toggle-all-marks)
-    (setq git-status-mode-map map)))
+    (setq git-status-mode-map map))
+  (easy-menu-define git-menu git-status-mode-map
+    "Git Menu"
+    `("Git"
+      ["Refresh" git-refresh-status t]
+      ["Commit" git-commit-file t]
+      ("Merge"
+       ["Next Unmerged File" git-next-unmerged-file t]
+       ["Prev Unmerged File" git-prev-unmerged-file t]
+       ["Mark as Resolved" git-resolve-file t]
+       ["Interactive Merge File" git-find-file-imerge t]
+       ["Diff Against Common Base File" git-diff-file-base t]
+       ["Diff Combined" git-diff-file-combined t]
+       ["Diff Against Merge Head" git-diff-file-merge-head t]
+       ["Diff Against Mine" git-diff-file-mine t]
+       ["Diff Against Other" git-diff-file-other t])
+      "--------"
+      ["Add File" git-add-file t]
+      ["Revert File" git-revert-file t]
+      ["Ignore File" git-ignore-file t]
+      ["Remove File" git-remove-file t]
+      "--------"
+      ["Find File" git-find-file t]
+      ["View File" git-view-file t]
+      ["Diff File" git-diff-file t]
+      ["Interactive Diff File" git-diff-file-idiff t]
+      ["Log" git-log-file t]
+      "--------"
+      ["Mark" git-mark-file t]
+      ["Mark All" git-mark-all t]
+      ["Unmark" git-unmark-file t]
+      ["Unmark All" git-unmark-all t]
+      ["Toggle All Marks" git-toggle-all-marks t]
+      ["Hide Handled Files" git-remove-handled t]
+      "--------"
+      ["Show Uptodate Files" git-toggle-show-uptodate :style toggle :selected git-show-uptodate]
+      ["Show Ignored Files" git-toggle-show-ignored :style toggle :selected git-show-ignored]
+      ["Show Unknown Files" git-toggle-show-unknown :style toggle :selected git-show-unknown]
+      "--------"
+      ["Quit" git-status-quit t])))
+
 
 ;; git mode should only run in the *git status* buffer
 (put 'git-status-mode 'mode-class 'special)
diff --git a/diff.c b/diff.c
index be6cf687a4421acdc127df73aafef4aa9cf4daac..9c79ee289151e9fc3eb1aaad91dd979bfee63102 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -454,6 +454,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
 struct emit_callback {
        struct xdiff_emit_state xm;
        int nparents, color_diff;
+       unsigned ws_rule;
        const char **label_path;
        struct diff_words_data *diff_words;
        int *found_changesp;
@@ -493,8 +494,8 @@ static void emit_line(const char *set, const char *reset, const char *line, int
 }
 
 static void emit_line_with_ws(int nparents,
-               const char *set, const char *reset, const char *ws,
-               const char *line, int len)
+                             const char *set, const char *reset, const char *ws,
+                             const char *line, int len, unsigned ws_rule)
 {
        int col0 = nparents;
        int last_tab_in_indent = -1;
@@ -502,13 +503,17 @@ static void emit_line_with_ws(int nparents,
        int i;
        int tail = len;
        int need_highlight_leading_space = 0;
-       /* The line is a newly added line.  Does it have funny leading
-        * whitespaces?  In indent, SP should never precede a TAB.
+       /*
+        * The line is a newly added line.  Does it have funny leading
+        * whitespaces?  In indent, SP should never precede a TAB.  In
+        * addition, under "indent with non tab" rule, there should not
+        * be more than 8 consecutive spaces.
         */
        for (i = col0; i < len; i++) {
                if (line[i] == '\t') {
                        last_tab_in_indent = i;
-                       if (0 <= last_space_in_indent)
+                       if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
+                           0 <= last_space_in_indent)
                                need_highlight_leading_space = 1;
                }
                else if (line[i] == ' ')
@@ -516,6 +521,13 @@ static void emit_line_with_ws(int nparents,
                else
                        break;
        }
+       if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
+           0 <= last_space_in_indent &&
+           last_tab_in_indent < 0 &&
+           8 <= (i - col0)) {
+               last_tab_in_indent = i;
+               need_highlight_leading_space = 1;
+       }
        fputs(set, stdout);
        fwrite(line, col0, 1, stdout);
        fputs(reset, stdout);
@@ -540,10 +552,12 @@ static void emit_line_with_ws(int nparents,
        tail = len - 1;
        if (line[tail] == '\n' && i < tail)
                tail--;
-       while (i < tail) {
-               if (!isspace(line[tail]))
-                       break;
-               tail--;
+       if (ws_rule & WS_TRAILING_SPACE) {
+               while (i < tail) {
+                       if (!isspace(line[tail]))
+                               break;
+                       tail--;
+               }
        }
        if ((i < tail && line[tail + 1] != '\n')) {
                /* This has whitespace between tail+1..len */
@@ -565,7 +579,7 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
                emit_line(set, reset, line, len);
        else
                emit_line_with_ws(ecbdata->nparents, set, reset, ws,
-                               line, len);
+                                 line, len, ecbdata->ws_rule);
 }
 
 static void fn_out_consume(void *priv, char *line, unsigned long len)
@@ -720,7 +734,9 @@ struct diffstat_t {
        int nr;
        int alloc;
        struct diffstat_file {
+               char *from_name;
                char *name;
+               char *print_name;
                unsigned is_unmerged:1;
                unsigned is_binary:1;
                unsigned is_renamed:1;
@@ -741,11 +757,14 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
        }
        diffstat->files[diffstat->nr++] = x;
        if (name_b) {
-               x->name = pprint_rename(name_a, name_b);
+               x->from_name = xstrdup(name_a);
+               x->name = xstrdup(name_b);
                x->is_renamed = 1;
        }
-       else
+       else {
+               x->from_name = NULL;
                x->name = xstrdup(name_a);
+       }
        return x;
 }
 
@@ -789,6 +808,28 @@ static void show_graph(char ch, int cnt, const char *set, const char *reset)
        printf("%s", reset);
 }
 
+static void fill_print_name(struct diffstat_file *file)
+{
+       char *pname;
+
+       if (file->print_name)
+               return;
+
+       if (!file->is_renamed) {
+               struct strbuf buf;
+               strbuf_init(&buf, 0);
+               if (quote_c_style(file->name, &buf, NULL, 0)) {
+                       pname = strbuf_detach(&buf, NULL);
+               } else {
+                       pname = file->name;
+                       strbuf_release(&buf);
+               }
+       } else {
+               pname = pprint_rename(file->from_name, file->name);
+       }
+       file->print_name = pname;
+}
+
 static void show_stats(struct diffstat_t* data, struct diff_options *options)
 {
        int i, len, add, del, total, adds = 0, dels = 0;
@@ -822,19 +863,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
                int change = file->added + file->deleted;
-
-               if (!file->is_renamed) {  /* renames are already quoted by pprint_rename */
-                       struct strbuf buf;
-                       strbuf_init(&buf, 0);
-                       if (quote_c_style(file->name, &buf, NULL, 0)) {
-                               free(file->name);
-                               file->name = strbuf_detach(&buf, NULL);
-                       } else {
-                               strbuf_release(&buf);
-                       }
-               }
-
-               len = strlen(file->name);
+               fill_print_name(file);
+               len = strlen(file->print_name);
                if (max_len < len)
                        max_len = len;
 
@@ -859,7 +889,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
 
        for (i = 0; i < data->nr; i++) {
                const char *prefix = "";
-               char *name = data->files[i]->name;
+               char *name = data->files[i]->print_name;
                int added = data->files[i]->added;
                int deleted = data->files[i]->deleted;
                int name_len;
@@ -887,17 +917,17 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                        printf("%s%d%s", add_c, added, reset);
                        printf(" bytes");
                        printf("\n");
-                       goto free_diffstat_file;
+                       continue;
                }
                else if (data->files[i]->is_unmerged) {
                        show_name(prefix, name, len, reset, set);
                        printf("  Unmerged\n");
-                       goto free_diffstat_file;
+                       continue;
                }
                else if (!data->files[i]->is_renamed &&
                         (added + deleted == 0)) {
                        total_files--;
-                       goto free_diffstat_file;
+                       continue;
                }
 
                /*
@@ -919,11 +949,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
                show_graph('+', add, add_c, reset);
                show_graph('-', del, del_c, reset);
                putchar('\n');
-       free_diffstat_file:
-               free(data->files[i]->name);
-               free(data->files[i]);
        }
-       free(data->files);
        printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
               set, total_files, adds, dels, reset);
 }
@@ -948,11 +974,7 @@ static void show_shortstats(struct diffstat_t* data)
                                dels += deleted;
                        }
                }
-               free(data->files[i]->name);
-               free(data->files[i]);
        }
-       free(data->files);
-
        printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
               total_files, adds, dels);
 }
@@ -961,6 +983,9 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
 {
        int i;
 
+       if (data->nr == 0)
+               return;
+
        for (i = 0; i < data->nr; i++) {
                struct diffstat_file *file = data->files[i];
 
@@ -968,19 +993,44 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
                        printf("-\t-\t");
                else
                        printf("%d\t%d\t", file->added, file->deleted);
-               if (!file->is_renamed) {
-                       write_name_quoted(file->name, stdout, options->line_termination);
+               if (options->line_termination) {
+                       fill_print_name(file);
+                       if (!file->is_renamed)
+                               write_name_quoted(file->name, stdout,
+                                                 options->line_termination);
+                       else {
+                               fputs(file->print_name, stdout);
+                               putchar(options->line_termination);
+                       }
                } else {
-                       fputs(file->name, stdout);
-                       putchar(options->line_termination);
+                       if (file->is_renamed) {
+                               putchar('\0');
+                               write_name_quoted(file->from_name, stdout, '\0');
+                       }
+                       write_name_quoted(file->name, stdout, '\0');
                }
        }
 }
 
+static void free_diffstat_info(struct diffstat_t *diffstat)
+{
+       int i;
+       for (i = 0; i < diffstat->nr; i++) {
+               struct diffstat_file *f = diffstat->files[i];
+               if (f->name != f->print_name)
+                       free(f->print_name);
+               free(f->name);
+               free(f->from_name);
+               free(f);
+       }
+       free(diffstat->files);
+}
+
 struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
        int lineno, color_diff;
+       unsigned ws_rule;
 };
 
 static void checkdiff_consume(void *priv, char *line, unsigned long len)
@@ -994,13 +1044,20 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0;
 
                /* check space before tab */
-               for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
+               for (i = 1; i < len; i++) {
                        if (line[i] == ' ')
                                spaces++;
-               if (line[i - 1] == '\t' && spaces)
-                       space_before_tab = 1;
+                       else if (line[i] == '\t') {
+                               if (spaces) {
+                                       space_before_tab = 1;
+                                       break;
+                               }
+                       }
+                       else
+                               break;
+               }
 
-               /* check white space at line end */
+               /* check whitespace at line end */
                if (line[len - 1] == '\n')
                        len--;
                if (isspace(line[len - 1]))
@@ -1014,9 +1071,10 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
                                        putchar(',');
                        }
                        if (white_space_at_end)
-                               printf("white space at end");
+                               printf("whitespace at end");
                        printf(":%s ", reset);
-                       emit_line_with_ws(1, set, reset, ws, line, len);
+                       emit_line_with_ws(1, set, reset, ws, line, len,
+                                         data->ws_rule);
                }
 
                data->lineno++;
@@ -1317,6 +1375,7 @@ static void builtin_diff(const char *name_a,
                ecbdata.label_path = lbl;
                ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
                ecbdata.found_changesp = &o->found_changes;
+               ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1410,6 +1469,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
+       data.ws_rule = whitespace_rule(data.filename);
 
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@ -2925,8 +2985,9 @@ void diff_flush(struct diff_options *options)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
                        show_stats(&diffstat, options);
-               else if (output_format & DIFF_FORMAT_SHORTSTAT)
+               if (output_format & DIFF_FORMAT_SHORTSTAT)
                        show_shortstats(&diffstat);
+               free_diffstat_info(&diffstat);
                separator++;
        }
 
index 1dab72ec1525ca6dd15ca20666cd91506b85890c..18a1c4eec49bfddcb81d6669c83f8f97a218d7bf 100644 (file)
@@ -31,11 +31,11 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
 char *pager_program;
-int pager_in_use;
 int pager_use_color = 1;
 char *editor_program;
 char *excludes_file;
 int auto_crlf = 0;     /* 1: both ways, -1: only when adding git objects */
+unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
index f6d58ac044e5afb09855d687d3c1bcc8e2273be3..5621c69d86062c7c75c0b8c2749d34efc78cafb4 100755 (executable)
@@ -2,13 +2,13 @@
 
 OPTIONS_KEEPDASHDASH=t
 OPTIONS_SPEC="\
-git-branch [options] [<branch>] [<paths>...]
+git-checkout [options] [<branch>] [<paths>...]
 --
 b=          create a new branch started at <branch>
-l           create the new branchs reflog
-track       tells if the new branch should track the remote branch
+l           create the new branch's reflog
+track       arrange that the new branch tracks the remote branch
 f           proceed even if the index or working tree is not HEAD
-m           performa  three-way merge on local modifications if needed
+m           merge local modifications into the new branch
 q,quiet     be quiet
 "
 SUBDIRECTORY_OK=Sometimes
index ecf9d89a108484af3f73817e60c523f1dca8fff0..68085a3225cb0047ab3c5764472d3d024e467160 100755 (executable)
@@ -205,7 +205,10 @@ fi
 # it is local
 if base=$(get_repo_base "$repo"); then
        repo="$base"
-       local=yes
+       if test -z "$depth"
+       then
+               local=yes
+       fi
 fi
 
 dir="$2"
@@ -297,7 +300,8 @@ yes)
                                      find objects -type f -print | sed -e 1q)
                        # objects directory should not be empty because
                        # we are cloning!
-                       test -f "$repo/$sample_file" || exit
+                       test -f "$repo/$sample_file" ||
+                               die "fatal: cannot clone empty repository"
                        if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
                        then
                                rm -f "$GIT_DIR/objects/sample"
index 1e7727d2763e744b94f55c2056eccaab12367c9e..9ee3f804520eadb322de3d0ea70a2bf9f092b089 100755 (executable)
@@ -80,6 +80,10 @@ case "${1:-.}${2:-.}${3:-.}" in
                echo "ERROR: $4: Not merging symbolic link changes."
                exit 1
                ;;
+       *,160000,*)
+               echo "ERROR: $4: Not merging conflicting submodule changes."
+               exit 1
+               ;;
        esac
 
        src2=`git-unpack-file $3`
index 5587c5ecea0f6a97f2715636890c9e2f89845d52..2f31fa2417300180662b66afdde11bdf1d6febb2 100755 (executable)
@@ -152,10 +152,11 @@ merge_file () {
        exit 1
     fi
 
-    BACKUP="$path.BACKUP.$$"
-    LOCAL="$path.LOCAL.$$"
-    REMOTE="$path.REMOTE.$$"
-    BASE="$path.BASE.$$"
+    ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
+    BACKUP="$path.BACKUP.$ext"
+    LOCAL="$path.LOCAL.$ext"
+    REMOTE="$path.REMOTE.$ext"
+    BASE="$path.BASE.$ext"
 
     mv -- "$path" "$BACKUP"
     cp -- "$BACKUP" "$path"
index 76baa8e431e082c1e36eb9c78125094de0b8a85e..1d6f46645328dc2520226aba532c4acb86f5ec32 100755 (executable)
@@ -367,8 +367,10 @@ sub expand_aliases {
        } while (!defined $_);
 
        $initial_reply_to = $_;
-       $initial_reply_to =~ s/^\s+<?/</;
-       $initial_reply_to =~ s/>?\s+$/>/;
+}
+if (defined $initial_reply_to && $_ ne "") {
+       $initial_reply_to =~ s/^\s*<?/</;
+       $initial_reply_to =~ s/>?\s*$/>/;
 }
 
 if (!defined $smtp_server) {
index 9f884eb2132c76b86475b97256e0ed565f84bb2e..54d784469af7b787190e80d277abf2a4782c7d3f 100755 (executable)
@@ -529,7 +529,7 @@ sub cmd_find_rev {
                            "$head history\n";
                }
                my $desired_revision = substr($revision_or_hash, 1);
-               $result = $gs->rev_db_get($desired_revision);
+               $result = $gs->rev_map_get($desired_revision);
        } else {
                my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
                $result = $rev;
@@ -1128,12 +1128,12 @@ sub working_head_info {
                if (defined $url && defined $rev) {
                        next if $max{$url} and $max{$url} < $rev;
                        if (my $gs = Git::SVN->find_by_url($url)) {
-                               my $c = $gs->rev_db_get($rev);
+                               my $c = $gs->rev_map_get($rev);
                                if ($c && $c eq $hash) {
                                        close $fh; # break the pipe
                                        return ($url, $rev, $uuid, $gs);
                                } else {
-                                       $max{$url} ||= $gs->rev_db_max;
+                                       $max{$url} ||= $gs->rev_map_max;
                                }
                        }
                }
@@ -1234,6 +1234,8 @@ sub md5sum {
 package Git::SVN;
 use strict;
 use warnings;
+use Fcntl qw/:DEFAULT :seek/;
+use constant rev_map_fmt => 'NH40';
 use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
             $_repack $_repack_flags $_use_svm_props $_head
             $_use_svnsync_props $no_reuse_existing $_minimize_url
@@ -1362,7 +1364,7 @@ sub fetch_all {
        if ($fetch) {
                foreach my $p (sort keys %$fetch) {
                        my $gs = Git::SVN->new($fetch->{$p}, $repo_id, $p);
-                       my $lr = $gs->rev_db_max;
+                       my $lr = $gs->rev_map_max;
                        if (defined $lr) {
                                $base = $lr if ($lr < $base);
                        }
@@ -1897,38 +1899,20 @@ sub last_rev_commit {
                        return ($rev, $c);
                }
        }
-       my $db_path = $self->db_path;
-       unless (-e $db_path) {
+       my $map_path = $self->map_path;
+       unless (-e $map_path) {
                ($self->{last_rev}, $self->{last_commit}) = (undef, undef);
                return (undef, undef);
        }
-       my $offset = -41; # from tail
-       my $rl;
-       open my $fh, '<', $db_path or croak "$db_path not readable: $!\n";
-       sysseek($fh, $offset, 2); # don't care for errors
-       sysread($fh, $rl, 41) == 41 or return (undef, undef);
-       chomp $rl;
-       while (('0' x40) eq $rl && sysseek($fh, 0, 1) != 0) {
-               $offset -= 41;
-               sysseek($fh, $offset, 2); # don't care for errors
-               sysread($fh, $rl, 41) == 41 or return (undef, undef);
-               chomp $rl;
-       }
-       if ($c && $c ne $rl) {
-               die "$db_path and ", $self->refname,
-                   " inconsistent!:\n$c != $rl\n";
-       }
-       my $rev = sysseek($fh, 0, 1) or croak $!;
-       $rev =  ($rev - 41) / 41;
-       close $fh or croak $!;
-       ($self->{last_rev}, $self->{last_commit}) = ($rev, $c);
-       return ($rev, $c);
+       my ($rev, $commit) = $self->rev_map_max(1);
+       ($self->{last_rev}, $self->{last_commit}) = ($rev, $commit);
+       return ($rev, $commit);
 }
 
 sub get_fetch_range {
        my ($self, $min, $max) = @_;
        $max ||= $self->ra->get_latest_revnum;
-       $min ||= $self->rev_db_max;
+       $min ||= $self->rev_map_max;
        (++$min, $max);
 }
 
@@ -2073,7 +2057,7 @@ sub do_git_commit {
                    " was r$lr, but we are about to fetch: ",
                    "r$log_entry->{revision}!\n";
        }
-       if (my $c = $self->rev_db_get($log_entry->{revision})) {
+       if (my $c = $self->rev_map_get($log_entry->{revision})) {
                croak "$log_entry->{revision} = $c already exists! ",
                      "Why are we refetching it?\n";
        }
@@ -2116,14 +2100,14 @@ sub do_git_commit {
                die "Failed to commit, invalid sha1: $commit\n";
        }
 
-       $self->rev_db_set($log_entry->{revision}, $commit, 1);
+       $self->rev_map_set($log_entry->{revision}, $commit, 1);
 
        $self->{last_rev} = $log_entry->{revision};
        $self->{last_commit} = $commit;
        print "r$log_entry->{revision}";
        if (defined $log_entry->{svm_revision}) {
                 print " (\@$log_entry->{svm_revision})";
-                $self->rev_db_set($log_entry->{svm_revision}, $commit,
+                $self->rev_map_set($log_entry->{svm_revision}, $commit,
                                   0, $self->svm_uuid);
        }
        print " = $commit ($self->{ref_id})\n";
@@ -2465,25 +2449,44 @@ sub set_tree {
        }
 }
 
+sub rebuild_from_rev_db {
+       my ($self, $path) = @_;
+       my $r = -1;
+       open my $fh, '<', $path or croak "open: $!";
+       while (<$fh>) {
+               length($_) == 41 or croak "inconsistent size in ($_) != 41";
+               chomp($_);
+               ++$r;
+               next if $_ eq ('0' x 40);
+               $self->rev_map_set($r, $_);
+               print "r$r = $_\n";
+       }
+       close $fh or croak "close: $!";
+       unlink $path or croak "unlink: $!";
+}
+
 sub rebuild {
        my ($self) = @_;
-       my $db_path = $self->db_path;
-       return if (-e $db_path && ! -z $db_path);
+       my $map_path = $self->map_path;
+       return if (-e $map_path && ! -z $map_path);
        return unless ::verify_ref($self->refname.'^0');
-       if (-f $self->{db_root}) {
-               rename $self->{db_root}, $db_path or die
-                    "rename $self->{db_root} => $db_path failed: $!\n";
-               my ($dir, $base) = ($db_path =~ m#^(.*?)/?([^/]+)$#);
-               symlink $base, $self->{db_root} or die
-                    "symlink $base => $self->{db_root} failed: $!\n";
+       if ($self->use_svm_props || $self->no_metadata) {
+               my $rev_db = $self->rev_db_path;
+               $self->rebuild_from_rev_db($rev_db);
+               if ($self->use_svm_props) {
+                       my $svm_rev_db = $self->rev_db_path($self->svm_uuid);
+                       $self->rebuild_from_rev_db($svm_rev_db);
+               }
+               $self->unlink_rev_db_symlink;
                return;
        }
-       print "Rebuilding $db_path ...\n";
-       my ($log, $ctx) = command_output_pipe("log", '--no-color', $self->refname);
-       my $latest;
+       print "Rebuilding $map_path ...\n";
+       my ($log, $ctx) =
+           command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
+                               $self->refname, '--');
        my $full_url = $self->full_url;
        remove_username($full_url);
-       my $svn_uuid;
+       my $svn_uuid = $self->ra_uuid;
        my $c;
        while (<$log>) {
                if ( m{^commit ($::sha1)$} ) {
@@ -2499,46 +2502,85 @@ sub rebuild {
 
                # if we merged or otherwise started elsewhere, this is
                # how we break out of it
-               if ((defined $svn_uuid && ($uuid ne $svn_uuid)) ||
+               if (($uuid ne $svn_uuid) ||
                    ($full_url && $url && ($url ne $full_url))) {
                        next;
                }
-               $latest ||= $rev;
-               $svn_uuid ||= $uuid;
 
-               $self->rev_db_set($rev, $c);
+               $self->rev_map_set($rev, $c);
                print "r$rev = $c\n";
        }
        command_close_pipe($log, $ctx);
-       print "Done rebuilding $db_path\n";
+       print "Done rebuilding $map_path\n";
+       my $rev_db_path = $self->rev_db_path;
+       if (-f $self->rev_db_path) {
+               unlink $self->rev_db_path or croak "unlink: $!";
+       }
+       $self->unlink_rev_db_symlink;
 }
 
-# rev_db:
+# rev_map:
 # Tie::File seems to be prone to offset errors if revisions get sparse,
 # it's not that fast, either.  Tie::File is also not in Perl 5.6.  So
 # one of my favorite modules is out :<  Next up would be one of the DBM
-# modules, but I'm not sure which is most portable...  So I'll just
-# go with something that's plain-text, but still capable of
-# being randomly accessed.  So here's my ultra-simple fixed-width
-# database.  All records are 40 characters + "\n", so it's easy to seek
-# to a revision: (41 * rev) is the byte offset.
-# A record of 40 0s denotes an empty revision.
-# And yes, it's still pretty fast (faster than Tie::File).
+# modules, but I'm not sure which is most portable...
+#
+# This is the replacement for the rev_db format, which was too big
+# and inefficient for large repositories with a lot of sparse history
+# (mainly tags)
+#
+# The format is this:
+#   - 24 bytes for every record,
+#     * 4 bytes for the integer representing an SVN revision number
+#     * 20 bytes representing the sha1 of a git commit
+#   - No empty padding records like the old format
+#     (except the last record, which can be overwritten)
+#   - new records are written append-only since SVN revision numbers
+#     increase monotonically
+#   - lookups on SVN revision number are done via a binary search
+#   - Piping the file to xxd -c24 is a good way of dumping it for
+#     viewing or editing (piped back through xxd -r), should the need
+#     ever arise.
+#   - The last record can be padding revision with an all-zero sha1
+#     This is used to optimize fetch performance when using multiple
+#     "fetch" directives in .git/config
+#
 # These files are disposable unless noMetadata or useSvmProps is set
 
-sub _rev_db_set {
+sub _rev_map_set {
        my ($fh, $rev, $commit) = @_;
-       my $offset = $rev * 41;
-       # assume that append is the common case:
-       seek $fh, 0, 2 or croak $!;
-       my $pos = tell $fh;
-       if ($pos < $offset) {
-               for (1 .. (($offset - $pos) / 41)) {
-                       print $fh (('0' x 40),"\n") or croak $!;
+
+       my $size = (stat($fh))[7];
+       ($size % 24) == 0 or croak "inconsistent size: $size";
+
+       my $wr_offset = 0;
+       if ($size > 0) {
+               sysseek($fh, -24, SEEK_END) or croak "seek: $!";
+               my $read = sysread($fh, my $buf, 24) or croak "read: $!";
+               $read == 24 or croak "read only $read bytes (!= 24)";
+               my ($last_rev, $last_commit) = unpack(rev_map_fmt, $buf);
+               if ($last_commit eq ('0' x40)) {
+                       if ($size >= 48) {
+                               sysseek($fh, -48, SEEK_END) or croak "seek: $!";
+                               $read = sysread($fh, $buf, 24) or
+                                   croak "read: $!";
+                               $read == 24 or
+                                   croak "read only $read bytes (!= 24)";
+                               ($last_rev, $last_commit) =
+                                   unpack(rev_map_fmt, $buf);
+                               if ($last_commit eq ('0' x40)) {
+                                       croak "inconsistent .rev_map\n";
+                               }
+                       }
+                       if ($last_rev >= $rev) {
+                               croak "last_rev is higher!: $last_rev >= $rev";
+                       }
+                       $wr_offset = -24;
                }
        }
-       seek $fh, $offset, 0 or croak $!;
-       print $fh $commit,"\n" or croak $!;
+       sysseek($fh, $wr_offset, SEEK_END) or croak "seek: $!";
+       syswrite($fh, pack(rev_map_fmt, $rev, $commit), 24) == 24 or
+         croak "write: $!";
 }
 
 sub mkfile {
@@ -2551,10 +2593,10 @@ sub mkfile {
        }
 }
 
-sub rev_db_set {
+sub rev_map_set {
        my ($self, $rev, $commit, $update_ref, $uuid) = @_;
        length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
-       my $db = $self->db_path($uuid);
+       my $db = $self->map_path($uuid);
        my $db_lock = "$db.lock";
        my $sig;
        if ($update_ref) {
@@ -2569,16 +2611,18 @@ sub rev_db_set {
        # and we can't afford to lose it because rebuild() won't work
        if ($self->use_svm_props || $self->no_metadata) {
                $sync = 1;
-               copy($db, $db_lock) or die "rev_db_set(@_): ",
+               copy($db, $db_lock) or die "rev_map_set(@_): ",
                                           "Failed to copy: ",
                                           "$db => $db_lock ($!)\n";
        } else {
-               rename $db, $db_lock or die "rev_db_set(@_): ",
+               rename $db, $db_lock or die "rev_map_set(@_): ",
                                            "Failed to rename: ",
                                            "$db => $db_lock ($!)\n";
        }
-       open my $fh, '+<', $db_lock or die "Couldn't open $db_lock: $!\n";
-       _rev_db_set($fh, $rev, $commit);
+
+       sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
+            or croak "Couldn't open $db_lock: $!\n";
+       _rev_map_set($fh, $rev, $commit);
        if ($sync) {
                $fh->flush or die "Couldn't flush $db_lock: $!\n";
                $fh->sync or die "Couldn't sync $db_lock: $!\n";
@@ -2589,7 +2633,7 @@ sub rev_db_set {
                command_noisy('update-ref', '-m', "r$rev",
                              $self->refname, $commit);
        }
-       rename $db_lock, $db or die "rev_db_set(@_): ", "Failed to rename: ",
+       rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ",
                                    "$db_lock => $db ($!)\n";
        delete $LOCKFILES{$db_lock};
        if ($update_ref) {
@@ -2599,29 +2643,76 @@ sub rev_db_set {
        }
 }
 
-sub rev_db_max {
-       my ($self) = @_;
+# If want_commit, this will return an array of (rev, commit) where
+# commit _must_ be a valid commit in the archive.
+# Otherwise, it'll return the max revision (whether or not the
+# commit is valid or just a 0x40 placeholder).
+sub rev_map_max {
+       my ($self, $want_commit) = @_;
        $self->rebuild;
-       my $db_path = $self->db_path;
-       my @stat = stat $db_path or return 0;
-       ($stat[7] % 41) == 0 or die "$db_path inconsistent size: $stat[7]\n";
-       my $max = $stat[7] / 41;
-       (($max > 0) ? $max - 1 : 0);
+       my $map_path = $self->map_path;
+       stat $map_path or return $want_commit ? (0, undef) : 0;
+       sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
+       my $size = (stat($fh))[7];
+       ($size % 24) == 0 or croak "inconsistent size: $size";
+
+       if ($size == 0) {
+               close $fh or croak "close: $!";
+               return $want_commit ? (0, undef) : 0;
+       }
+
+       sysseek($fh, -24, SEEK_END) or croak "seek: $!";
+       sysread($fh, my $buf, 24) == 24 or croak "read: $!";
+       my ($r, $c) = unpack(rev_map_fmt, $buf);
+       if ($want_commit && $c eq ('0' x40)) {
+               if ($size < 48) {
+                       return $want_commit ? (0, undef) : 0;
+               }
+               sysseek($fh, -48, SEEK_END) or croak "seek: $!";
+               sysread($fh, $buf, 24) == 24 or croak "read: $!";
+               ($r, $c) = unpack(rev_map_fmt, $buf);
+               if ($c eq ('0'x40)) {
+                       croak "Penultimate record is all-zeroes in $map_path";
+               }
+       }
+       close $fh or croak "close: $!";
+       $want_commit ? ($r, $c) : $r;
 }
 
-sub rev_db_get {
+sub rev_map_get {
        my ($self, $rev, $uuid) = @_;
-       my $ret;
-       my $offset = $rev * 41;
-       my $db_path = $self->db_path($uuid);
-       return undef unless -e $db_path;
-       open my $fh, '<', $db_path or croak $!;
-       if (sysseek($fh, $offset, 0) == $offset) {
-               my $read = sysread($fh, $ret, 40);
-               $ret = undef if ($read != 40 || $ret eq ('0'x40));
+       my $map_path = $self->map_path($uuid);
+       return undef unless -e $map_path;
+
+       sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
+       my $size = (stat($fh))[7];
+       ($size % 24) == 0 or croak "inconsistent size: $size";
+
+       if ($size == 0) {
+               close $fh or croak "close: $fh";
+               return undef;
        }
-       close $fh or croak $!;
-       $ret;
+
+       my ($l, $u) = (0, $size - 24);
+       my ($r, $c, $buf);
+
+       while ($l <= $u) {
+               my $i = int(($l/24 + $u/24) / 2) * 24;
+               sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
+               sysread($fh, my $buf, 24) == 24 or croak "read: $!";
+               my ($r, $c) = unpack('NH40', $buf);
+
+               if ($r < $rev) {
+                       $l = $i + 24;
+               } elsif ($r > $rev) {
+                       $u = $i - 24;
+               } else { # $r == $rev
+                       close($fh) or croak "close: $!";
+                       return $c eq ('0' x 40) ? undef : $c;
+               }
+       }
+       close($fh) or croak "close: $!";
+       undef;
 }
 
 # Finds the first svn revision that exists on (if $eq_ok is true) or
@@ -2633,7 +2724,7 @@ sub find_rev_before {
        --$rev unless $eq_ok;
        $min_rev ||= 1;
        while ($rev >= $min_rev) {
-               if (my $c = $self->rev_db_get($rev)) {
+               if (my $c = $self->rev_map_get($rev)) {
                        return ($rev, $c);
                }
                --$rev;
@@ -2648,9 +2739,9 @@ sub find_rev_before {
 sub find_rev_after {
        my ($self, $rev, $eq_ok, $max_rev) = @_;
        ++$rev unless $eq_ok;
-       $max_rev ||= $self->rev_db_max();
+       $max_rev ||= $self->rev_map_max;
        while ($rev <= $max_rev) {
-               if (my $c = $self->rev_db_get($rev)) {
+               if (my $c = $self->rev_map_get($rev)) {
                        return ($rev, $c);
                }
                ++$rev;
@@ -2673,13 +2764,32 @@ sub _new {
        bless {
                ref_id => $ref_id, dir => $dir, index => "$dir/index",
                path => $path, config => "$ENV{GIT_DIR}/svn/config",
-               db_root => "$dir/.rev_db", repo_id => $repo_id }, $class;
+               map_root => "$dir/.rev_map", repo_id => $repo_id }, $class;
 }
 
-sub db_path {
+# for read-only access of old .rev_db formats
+sub unlink_rev_db_symlink {
+       my ($self) = @_;
+       my $link = $self->rev_db_path;
+       $link =~ s/\.[\w-]+$// or croak "missing UUID at the end of $link";
+       if (-l $link) {
+               unlink $link or croak "unlink: $link failed!";
+       }
+}
+
+sub rev_db_path {
+       my ($self, $uuid) = @_;
+       my $db_path = $self->map_path($uuid);
+       $db_path =~ s{/\.rev_map\.}{/\.rev_db\.}
+           or croak "map_path: $db_path does not contain '/.rev_map.' !";
+       $db_path;
+}
+
+# the new replacement for .rev_db
+sub map_path {
        my ($self, $uuid) = @_;
        $uuid ||= $self->ra_uuid;
-       "$self->{db_root}.$uuid";
+       "$self->{map_root}.$uuid";
 }
 
 sub uri_encode {
@@ -3763,7 +3873,7 @@ sub gs_fetch_loop_common {
 
                        foreach my $gs ($self->match_globs(\%exists, $paths,
                                                           $globs, $r)) {
-                               if ($gs->rev_db_max >= $r) {
+                               if ($gs->rev_map_max >= $r) {
                                        next;
                                }
                                next unless $gs->match_paths($paths, $r);
@@ -3792,8 +3902,9 @@ sub gs_fetch_loop_common {
                # pre-fill the .rev_db since it'll eventually get filled in
                # with '0' x40 if something new gets committed
                foreach my $gs (@$gsv) {
-                       next if defined $gs->rev_db_get($max);
-                       $gs->rev_db_set($max, 0 x40);
+                       next if $gs->rev_map_max >= $max;
+                       next if defined $gs->rev_map_get($max);
+                       $gs->rev_map_set($max, 0 x40);
                }
                foreach my $g (@$globs) {
                        my $k = "svn-remote.$g->{remote}.$g->{t}-maxRev";
@@ -3969,39 +4080,7 @@ sub cmt_showable {
 }
 
 sub log_use_color {
-       return 1 if $color;
-       my ($dc, $dcvar);
-       $dcvar = 'color.diff';
-       $dc = `git-config --get $dcvar`;
-       if ($dc eq '') {
-               # nothing at all; fallback to "diff.color"
-               $dcvar = 'diff.color';
-               $dc = `git-config --get $dcvar`;
-       }
-       chomp($dc);
-       if ($dc eq 'auto') {
-               my $pc;
-               $pc = `git-config --get color.pager`;
-               if ($pc eq '') {
-                       # does not have it -- fallback to pager.color
-                       $pc = `git-config --bool --get pager.color`;
-               }
-               else {
-                       $pc = `git-config --bool --get color.pager`;
-                       if ($?) {
-                               $pc = 'false';
-                       }
-               }
-               chomp($pc);
-               if (-t *STDOUT || (defined $pager && $pc eq 'true')) {
-                       return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
-               }
-               return 0;
-       }
-       return 0 if $dc eq 'never';
-       return 1 if $dc eq 'always';
-       chomp($dc = `git-config --bool --get $dcvar`);
-       return ($dc eq 'true');
+       return $color || Git->repository->get_colorbool('color.diff');
 }
 
 sub git_svn_log_cmd {
@@ -4030,7 +4109,7 @@ sub git_svn_log_cmd {
        push @cmd, @log_opts;
        if (defined $r_max && $r_max == $r_min) {
                push @cmd, '--max-count=1';
-               if (my $c = $gs->rev_db_get($r_max)) {
+               if (my $c = $gs->rev_map_get($r_max)) {
                        push @cmd, $c;
                }
        } elsif (defined $r_max) {
@@ -4060,6 +4139,7 @@ sub config_pager {
        } elsif (length $pager == 0 || $pager eq 'cat') {
                $pager = undef;
        }
+       $ENV{GIT_PAGER_IN_USE} = defined($pager);
 }
 
 sub run_pager {
@@ -4311,6 +4391,16 @@ package Git::SVN::Migration;
 #              --use-separate-remotes option in git-clone (now default)
 #            - we do not automatically migrate to this (following
 #              the example set by core git)
+#
+# v5 layout: .rev_db.$UUID => .rev_map.$UUID
+#            - newer, more-efficient format that uses 24-bytes per record
+#              with no filler space.
+#            - use xxd -c24 < .rev_map.$UUID to view and debug
+#            - This is a one-way migration, repositories updated to the
+#              new format will not be able to use old git-svn without
+#              rebuilding the .rev_db.  Rebuilding the rev_db is not
+#              possible if noMetadata or useSvmProps are set; but should
+#              be no problem for users that use the (sensible) defaults.
 use strict;
 use warnings;
 use Carp qw/croak/;
index 78283b4de3b9111d87368df747daa96b5f6c09c3..fffbe9ccb42e305d898a25a126918db0883b11cb 100644 (file)
@@ -1275,8 +1275,6 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        char *ep;
        char timeout_header[25];
        struct remote_lock *lock = NULL;
-       XML_Parser parser = XML_ParserCreate(NULL);
-       enum XML_Status result;
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
 
@@ -1345,6 +1343,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
        if (start_active_slot(slot)) {
                run_active_slot(slot);
                if (results.curl_result == CURLE_OK) {
+                       XML_Parser parser = XML_ParserCreate(NULL);
+                       enum XML_Status result;
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
@@ -1363,6 +1363,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
                                                XML_GetErrorCode(parser)));
                                lock->timeout = -1;
                        }
+                       XML_ParserFree(parser);
                }
        } else {
                fprintf(stderr, "Unable to start LOCK request\n");
@@ -1525,8 +1526,6 @@ static void remote_ls(const char *path, int flags,
        struct buffer out_buffer;
        char *in_data;
        char *out_data;
-       XML_Parser parser = XML_ParserCreate(NULL);
-       enum XML_Status result;
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
        struct remote_ls_ctx ls;
@@ -1569,6 +1568,8 @@ static void remote_ls(const char *path, int flags,
        if (start_active_slot(slot)) {
                run_active_slot(slot);
                if (results.curl_result == CURLE_OK) {
+                       XML_Parser parser = XML_ParserCreate(NULL);
+                       enum XML_Status result;
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
@@ -1587,6 +1588,7 @@ static void remote_ls(const char *path, int flags,
                                        XML_ErrorString(
                                                XML_GetErrorCode(parser)));
                        }
+                       XML_ParserFree(parser);
                }
        } else {
                fprintf(stderr, "Unable to start PROPFIND request\n");
@@ -1620,8 +1622,6 @@ static int locking_available(void)
        struct buffer out_buffer;
        char *in_data;
        char *out_data;
-       XML_Parser parser = XML_ParserCreate(NULL);
-       enum XML_Status result;
        struct curl_slist *dav_headers = NULL;
        struct xml_ctx ctx;
        int lock_flags = 0;
@@ -1658,6 +1658,8 @@ static int locking_available(void)
        if (start_active_slot(slot)) {
                run_active_slot(slot);
                if (results.curl_result == CURLE_OK) {
+                       XML_Parser parser = XML_ParserCreate(NULL);
+                       enum XML_Status result;
                        ctx.name = xcalloc(10, 1);
                        ctx.len = 0;
                        ctx.cdata = NULL;
@@ -1676,6 +1678,7 @@ static int locking_available(void)
                                                XML_GetErrorCode(parser)));
                                lock_flags = 0;
                        }
+                       XML_ParserFree(parser);
                }
        } else {
                fprintf(stderr, "Unable to start PROPFIND request\n");
diff --git a/http.c b/http.c
index e4aa9c19dbe7fd2ed948d5cca255a7db44a9472f..146f62609dbf75cf8b6d873f040e1386d73b57d1 100644 (file)
--- a/http.c
+++ b/http.c
@@ -4,31 +4,31 @@ int data_received;
 int active_requests = 0;
 
 #ifdef USE_CURL_MULTI
-int max_requests = -1;
-CURLM *curlm;
+static int max_requests = -1;
+static CURLM *curlm;
 #endif
 #ifndef NO_CURL_EASY_DUPHANDLE
-CURL *curl_default;
+static CURL *curl_default;
 #endif
 char curl_errorstr[CURL_ERROR_SIZE];
 
-int curl_ssl_verify = -1;
-char *ssl_cert = NULL;
+static int curl_ssl_verify = -1;
+static char *ssl_cert = NULL;
 #if LIBCURL_VERSION_NUM >= 0x070902
-char *ssl_key = NULL;
+static char *ssl_key = NULL;
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
-char *ssl_capath = NULL;
+static char *ssl_capath = NULL;
 #endif
-char *ssl_cainfo = NULL;
-long curl_low_speed_limit = -1;
-long curl_low_speed_time = -1;
-int curl_ftp_no_epsv = 0;
-char *curl_http_proxy = NULL;
+static char *ssl_cainfo = NULL;
+static long curl_low_speed_limit = -1;
+static long curl_low_speed_time = -1;
+static int curl_ftp_no_epsv = 0;
+static char *curl_http_proxy = NULL;
 
-struct curl_slist *pragma_header;
+static struct curl_slist *pragma_header;
 
-struct active_request_slot *active_queue_head = NULL;
+static struct active_request_slot *active_queue_head = NULL;
 
 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
                           struct buffer *buffer)
diff --git a/http.h b/http.h
index 72abac20f856b45c873cc370f23c7df08b650370..fe1b0d153bcd80d12a405c26e0f073cbf14dde34 100644 (file)
--- a/http.h
+++ b/http.h
@@ -80,24 +80,6 @@ extern void http_cleanup(void);
 extern int data_received;
 extern int active_requests;
 
-#ifndef NO_CURL_EASY_DUPHANDLE
-extern CURL *curl_default;
-#endif
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
-extern int curl_ssl_verify;
-extern char *ssl_cert;
-#if LIBCURL_VERSION_NUM >= 0x070902
-extern char *ssl_key;
-#endif
-#if LIBCURL_VERSION_NUM >= 0x070908
-extern char *ssl_capath;
-#endif
-extern char *ssl_cainfo;
-extern long curl_low_speed_limit;
-extern long curl_low_speed_time;
-
-extern struct curl_slist *pragma_header;
-extern struct curl_slist *no_range_header;
-
 #endif /* HTTP_H */
diff --git a/ident.c b/ident.c
index 7631698f27a8e1685614c233037eb067eef65168..892d77ac9366978fcb172e0d258410f271631012 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -182,14 +182,15 @@ static const char *env_hint =
 "Omit --global to set the identity only in this repository.\n"
 "\n";
 
-static const char *fmt_ident_1(const char *name, const char *email,
-                              const char *date_str, int flag)
+const char *fmt_ident(const char *name, const char *email,
+                     const char *date_str, int flag)
 {
        static char buffer[1000];
        char date[50];
        int i;
-       int error_on_no_name = !!(flag & 01);
-       int name_addr_only = !!(flag & 02);
+       int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
+       int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
+       int name_addr_only = (flag & IDENT_NO_DATE);
 
        setup_ident();
        if (!name)
@@ -200,12 +201,12 @@ static const char *fmt_ident_1(const char *name, const char *email,
        if (!*name) {
                struct passwd *pw;
 
-               if (0 <= error_on_no_name &&
+               if ((warn_on_no_name || error_on_no_name) &&
                    name == git_default_name && env_hint) {
                        fprintf(stderr, env_hint, au_env, co_env);
                        env_hint = NULL; /* warn only once, for "git-var -l" */
                }
-               if (0 < error_on_no_name)
+               if (error_on_no_name)
                        die("empty ident %s <%s> not allowed", name, email);
                pw = getpwuid(getuid());
                if (!pw)
@@ -234,30 +235,23 @@ static const char *fmt_ident_1(const char *name, const char *email,
        return buffer;
 }
 
-const char *fmt_ident(const char *name, const char *email,
-                     const char *date_str, int error_on_no_name)
-{
-       int flag = (error_on_no_name ? 01 : 0);
-       return fmt_ident_1(name, email, date_str, flag);
-}
-
 const char *fmt_name(const char *name, const char *email)
 {
-       return fmt_ident_1(name, email, NULL, 03);
+       return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
 }
 
-const char *git_author_info(int error_on_no_name)
+const char *git_author_info(int flag)
 {
        return fmt_ident(getenv("GIT_AUTHOR_NAME"),
                         getenv("GIT_AUTHOR_EMAIL"),
                         getenv("GIT_AUTHOR_DATE"),
-                        error_on_no_name);
+                        flag);
 }
 
-const char *git_committer_info(int error_on_no_name)
+const char *git_committer_info(int flag)
 {
        return fmt_ident(getenv("GIT_COMMITTER_NAME"),
                         getenv("GIT_COMMITTER_EMAIL"),
                         getenv("GIT_COMMITTER_DATE"),
-                        error_on_no_name);
+                        flag);
 }
index 9a1e2f269dc5eff3b7544507a1e483948cc1254d..2a58dad3f43590008045282614fb0f174f3733e5 100644 (file)
@@ -1046,14 +1046,16 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
-               } else {
-                       if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
-                               die("cannot merge modes?");
-
+               } else if (S_ISGITLINK(a->mode)) {
+                       result.clean = 0;
+                       hashcpy(result.sha, a->sha1);
+               } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
 
                        if (!sha_eq(a->sha1, b->sha1))
                                result.clean = 0;
+               } else {
+                       die("unsupported object type in the tree");
                }
        }
 
diff --git a/pager.c b/pager.c
index fb7a1a625abf07b0d896a270286ee422a700c14c..0376953cb1b8a4095346e0d12ecef6e7db9c48c9 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -5,6 +5,8 @@
  * something different on Windows, for example.
  */
 
+static int spawned_pager;
+
 static void run_pager(const char *pager)
 {
        /*
@@ -41,7 +43,7 @@ void setup_pager(void)
        else if (!*pager || !strcmp(pager, "cat"))
                return;
 
-       pager_in_use = 1; /* means we are emitting to terminal */
+       spawned_pager = 1; /* means we are emitting to terminal */
 
        if (pipe(fd) < 0)
                return;
@@ -70,3 +72,14 @@ void setup_pager(void)
        die("unable to execute pager '%s'", pager);
        exit(255);
 }
+
+int pager_in_use(void)
+{
+       const char *env;
+
+       if (spawned_pager)
+               return 1;
+
+       env = getenv("GIT_PAGER_IN_USE");
+       return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
+}
index 6aecd897f8fe5b69b1805e94a20c578c10246a4d..320253eb8e91eb1d914aa4e34f7d3af4649b9b39 100644 (file)
@@ -17,9 +17,6 @@ if ($@ || $Error::VERSION < 0.15009) {
        $pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm';
 }
 
-my %extra;
-$extra{DESTDIR} = $ENV{DESTDIR} if $ENV{DESTDIR};
-
 # redirect stdout, otherwise the message "Writing perl.mak for Git"
 # disrupts the output for the target 'instlibdir'
 open STDOUT, ">&STDERR";
@@ -29,6 +26,5 @@ WriteMakefile(
        VERSION_FROM    => 'Git.pm',
        PM              => \%pm,
        MAKEFILE        => 'perl.mak',
-       INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3',
-       %extra
+       INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
 );
diff --git a/refs.c b/refs.c
index 3e6e98c5eb20fc7a365d25b3abc275644b4c26f9..759924d0c93621d72918f29145ae0377f34e8e0c 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1094,7 +1094,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
        adjust_shared_perm(log_file);
 
        msglen = msg ? strlen(msg) : 0;
-       committer = git_committer_info(-1);
+       committer = git_committer_info(0);
        maxlen = strlen(committer) + msglen + 100;
        logrec = xmalloc(maxlen);
        len = sprintf(logrec, "%s %s %s\n",
diff --git a/setup.c b/setup.c
index e96a316452197ed4b224f0c6839f036a01082bc2..b59dbe7f51e8ffcbf2721d2ec7201400830e6830 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -388,7 +388,6 @@ int check_repository_format(void)
 const char *setup_git_directory(void)
 {
        const char *retval = setup_git_directory_gently(NULL);
-       check_repository_format();
 
        /* If the work tree is not the default one, recompute prefix */
        if (inside_work_tree < 0) {
index 8d4a44721380213369668ea74dc4dbe3cf360485..9ee35e79013f9b1f6ef3df6c9102368fa347c089 100644 (file)
@@ -82,3 +82,29 @@ stop_httpd () {
        test -z "$SVN_HTTPD_PORT" && return
        "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
 }
+
+convert_to_rev_db () {
+       perl -w -- - "$@" <<\EOF
+use strict;
+@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
+open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
+open my $rd, '<', $ARGV[0] or die "$!: couldn't open: $ARGV[0]";
+my $size = (stat($rd))[7];
+($size % 24) == 0 or die "Inconsistent size: $size";
+while (sysread($rd, my $buf, 24) == 24) {
+       my ($r, $c) = unpack('NH40', $buf);
+       my $offset = $r * 41;
+       seek $wr, 0, 2 or die $!;
+       my $pos = tell $wr;
+       if ($pos < $offset) {
+               for (1 .. (($offset - $pos) / 41)) {
+                       print $wr (('0' x 40),"\n") or die $!;
+               }
+       }
+       seek $wr, $offset, 0 or die $!;
+       print $wr $c,"\n" or die $!;
+}
+close $wr or die $!;
+close $rd or die $!;
+EOF
+}
index 79fdff3f3a87cfe45b7fefa96e350675bd4e048c..6adf9d11d03c3e9b1b7ee1ee46364f0e3a755016 100755 (executable)
@@ -117,4 +117,13 @@ EOF
 git diff -b > out
 test_expect_success 'another test, with -b' 'git diff expect out'
 
+
+test_expect_success 'check mixed spaces and tabs in indent' '
+
+       # This is indented with SP HT SP.
+       echo "   foo();" > x &&
+       git diff --check | grep "space before tab"
+
+'
+
 test_done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
new file mode 100755 (executable)
index 0000000..67e080b
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+test_description='diff whitespace error detection'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       git config diff.color.whitespace "blue reverse" &&
+       >F &&
+       git add F &&
+       echo "         Eight SP indent" >>F &&
+       echo "  HT and SP indent" >>F &&
+       echo "With trailing SP " >>F &&
+       echo "No problem" >>F
+
+'
+
+blue_grep='7;34m' ;# ESC [ 7 ; 3 4 m
+
+test_expect_success default '
+
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With error >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -trail' '
+
+       git config core.whitespace -trail
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -trail (attribute)' '
+
+       git config --unset core.whitespace
+       echo "F whitespace=-trail" >.gitattributes
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT error >/dev/null &&
+       grep With normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -space' '
+
+       rm -f .gitattributes
+       git config core.whitespace -space
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With error >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -space (attribute)' '
+
+       git config --unset core.whitespace
+       echo "F whitespace=-space" >.gitattributes
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight normal >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With error >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only' '
+
+       rm -f .gitattributes
+       git config core.whitespace indent,-trailing,-space
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight error >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only (attribute)' '
+
+       git config --unset core.whitespace
+       echo "F whitespace=indent,-trailing,-space" >.gitattributes
+       git diff --color >output
+       grep "$blue_grep" output >error
+       grep -v "$blue_grep" output >normal
+
+       grep Eight error >/dev/null &&
+       grep HT normal >/dev/null &&
+       grep With normal >/dev/null &&
+       grep No normal >/dev/null
+
+'
+
+test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
new file mode 100755 (executable)
index 0000000..85f3da2
--- /dev/null
@@ -0,0 +1,151 @@
+#!/bin/sh
+
+test_description='core.whitespace rules and git-apply'
+
+. ./test-lib.sh
+
+prepare_test_file () {
+
+       # A line that has character X is touched iff RULE is in effect:
+       #       X  RULE
+       #       !  trailing-space
+       #       @  space-before-tab
+       #       #  indent-with-non-tab
+       sed -e "s/_/ /g" -e "s/>/       /" <<-\EOF
+               An_SP in an ordinary line>and a HT.
+               >A HT.
+               _>A SP and a HT (@).
+               _>_A SP, a HT and a SP (@).
+               _______Seven SP.
+               ________Eight SP (#).
+               _______>Seven SP and a HT (@).
+               ________>Eight SP and a HT (@#).
+               _______>_Seven SP, a HT and a SP (@).
+               ________>_Eight SP, a HT and a SP (@#).
+               _______________Fifteen SP (#).
+               _______________>Fifteen SP and a HT (@#).
+               ________________Sixteen SP (#).
+               ________________>Sixteen SP and a HT (@#).
+               _____a__Five SP, a non WS, two SP.
+               A line with a (!) trailing SP_
+               A line with a (!) trailing HT>
+       EOF
+}
+
+apply_patch () {
+       >target &&
+       sed -e "s|\([ab]\)/file|\1/target|" <patch |
+       git apply "$@"
+}
+
+test_fix () {
+
+       # fix should not barf
+       apply_patch --whitespace=fix || return 1
+
+       # find touched lines
+       diff file target | sed -n -e "s/^> //p" >fixed
+
+       # the changed lines are all expeced to change
+       fixed_cnt=$(wc -l <fixed)
+       case "$1" in
+       '') expect_cnt=$fixed_cnt ;;
+       ?*) expect_cnt=$(grep "[$1]" <fixed | wc -l) ;;
+       esac
+       test $fixed_cnt -eq $expect_cnt || return 1
+
+       # and we are not missing anything
+       case "$1" in
+       '') expect_cnt=0 ;;
+       ?*) expect_cnt=$(grep "[$1]" <file | wc -l) ;;
+       esac
+       test $fixed_cnt -eq $expect_cnt || return 1
+
+       # Get the patch actually applied
+       git diff-files -p target >fixed-patch
+       test -s fixed-patch && return 0
+
+       # Make sure it is complaint-free
+       >target
+       git apply --whitespace=error-all <fixed-patch
+
+}
+
+test_expect_success setup '
+
+       >file &&
+       git add file &&
+       prepare_test_file >file &&
+       git diff-files -p >patch &&
+       >target &&
+       git add target
+
+'
+
+test_expect_success 'whitespace=nowarn, default rule' '
+
+       apply_patch --whitespace=nowarn &&
+       diff file target
+
+'
+
+test_expect_success 'whitespace=warn, default rule' '
+
+       apply_patch --whitespace=warn &&
+       diff file target
+
+'
+
+test_expect_success 'whitespace=error-all, default rule' '
+
+       apply_patch --whitespace=error-all && return 1
+       test -s target && return 1
+       : happy
+
+'
+
+test_expect_success 'whitespace=error-all, no rule' '
+
+       git config core.whitespace -trailing,-space-before,-indent &&
+       apply_patch --whitespace=error-all &&
+       diff file target
+
+'
+
+test_expect_success 'whitespace=error-all, no rule (attribute)' '
+
+       git config --unset core.whitespace &&
+       echo "target -whitespace" >.gitattributes &&
+       apply_patch --whitespace=error-all &&
+       diff file target
+
+'
+
+for t in - ''
+do
+       case "$t" in '') tt='!' ;; *) tt= ;; esac
+       for s in - ''
+       do
+               case "$s" in '') ts='@' ;; *) ts= ;; esac
+               for i in - ''
+               do
+                       case "$i" in '') ti='#' ;; *) ti= ;; esac
+                       rule=${t}trailing,${s}space,${i}indent
+
+                       rm -f .gitattributes
+                       test_expect_success "rule=$rule" '
+                               git config core.whitespace "$rule" &&
+                               test_fix "$tt$ts$ti"
+                       '
+
+                       test_expect_success "rule=$rule (attributes)" '
+                               git config --unset core.whitespace &&
+                               echo "target whitespace=$rule" >.gitattributes &&
+                               test_fix "$tt$ts$ti"
+                       '
+
+               done
+       done
+done
+
+test_done
index c7130c4dcc31dda1f70160c81804096676943af4..09d56e0839f042a74bee5c13ab251f19fcc2c76e 100755 (executable)
@@ -640,6 +640,46 @@ test_expect_success 'creating a signed tag with -m message should succeed' '
        git diff expect actual
 '
 
+get_tag_header u-signed-tag $commit commit $time >expect
+echo 'Another message' >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success 'sign with a given key id' '
+
+       git tag -u committer@example.com -m "Another message" u-signed-tag &&
+       get_tag_msg u-signed-tag >actual &&
+       git diff expect actual
+
+'
+
+test_expect_success 'sign with an unknown id (1)' '
+
+       ! git tag -u author@example.com -m "Another message" o-signed-tag
+
+'
+
+test_expect_success 'sign with an unknown id (2)' '
+
+       ! git tag -u DEADBEEF -m "Another message" o-signed-tag
+
+'
+
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+test -n "$1" && exec >"$1"
+echo A signed tag message
+echo from a fake editor.
+EOF
+chmod +x fakeeditor
+
+get_tag_header implied-sign $commit commit $time >expect
+./fakeeditor >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success '-u implies signed tag' '
+       GIT_EDITOR=./fakeeditor git-tag -u CDDE430D implied-sign &&
+       get_tag_msg implied-sign >actual &&
+       git diff expect actual
+'
+
 cat >sigmsgfile <<EOF
 Another signed tag
 message in a file.
@@ -667,13 +707,6 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
        git diff expect actual
 '
 
-cat >fakeeditor <<'EOF'
-#!/bin/sh
-test -n "$1" && exec >"$1"
-echo A signed tag message
-echo from a fake editor.
-EOF
-chmod +x fakeeditor
 get_tag_header implied-annotate $commit commit $time >expect
 ./fakeeditor >>expect
 echo '-----BEGIN PGP SIGNATURE-----' >>expect
index 55558aba8b862b93cb8ad97a681a4749bae3b3e1..73d8a00e2cca907e562c50c70a10c02e3c0e02ca 100755 (executable)
@@ -20,6 +20,8 @@ Test switching across them.
 
 . ./test-lib.sh
 
+test_tick
+
 fill () {
        for i
        do
@@ -30,9 +32,10 @@ fill () {
 
 test_expect_success setup '
 
+       fill x y z > same &&
        fill 1 2 3 4 5 6 7 8 >one &&
        fill a b c d e >two &&
-       git add one two &&
+       git add same one two &&
        git commit -m "Initial A one, A two" &&
 
        git checkout -b renamer &&
@@ -74,16 +77,44 @@ test_expect_success "checkout with dirty tree without -m" '
 
 '
 
+test_expect_success "checkout with unrelated dirty tree without -m" '
+
+       git checkout -f master &&
+       fill 0 1 2 3 4 5 6 7 8 >same &&
+       cp same kept
+       git checkout side >messages &&
+       git diff same kept
+       (cat > messages.expect <<EOF
+M      same
+EOF
+) &&
+       touch messages.expect &&
+       git diff messages.expect messages
+'
+
 test_expect_success "checkout -m with dirty tree" '
 
        git checkout -f master &&
        git clean -f &&
 
        fill 0 1 2 3 4 5 6 7 8 >one &&
-       git checkout -m side &&
+       git checkout -m side > messages &&
 
        test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
 
+       (cat >expect.messages <<EOF
+Merging side with local
+Merging:
+ab76817 Side M one, D two, A three
+virtual local
+found 1 common ancestor(s):
+7329388 Initial A one, A two
+Auto-merged one
+M      one
+EOF
+) &&
+       git diff expect.messages messages &&
+
        fill "M one" "A three" "D       two" >expect.master &&
        git diff --name-status master >current.master &&
        diff expect.master current.master &&
@@ -145,7 +176,16 @@ test_expect_success 'checkout -m with merge conflict' '
 test_expect_success 'checkout to detach HEAD' '
 
        git checkout -f renamer && git clean -f &&
-       git checkout renamer^ &&
+       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
+) &&
+       git diff messages.expect messages &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
        test "z$H" = "z$M" &&
index 19c4b2c5566dcd6a88b8b557d4d597079df21b4e..05aa97d6f3d3a87d94004b9f9f4394d8e35175f6 100755 (executable)
@@ -4,7 +4,7 @@
 #
 
 # FIXME: Test the various index usages, -i and -o, test reflog,
-# signoff, hooks
+# signoff
 
 test_description='git-commit'
 . ./test-lib.sh
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
new file mode 100755 (executable)
index 0000000..d787cac
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='pre-commit hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+       echo "foo" > file &&
+       git add file &&
+       git commit -m "first"
+
+'
+
+test_expect_success '--no-verify with no hook' '
+
+       echo "bar" > file &&
+       git add file &&
+       git commit --no-verify -m "bar"
+
+'
+
+# now install hook that always succeeds
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/pre-commit"
+mkdir -p "$HOOKDIR"
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 0
+EOF
+chmod +x "$HOOK"
+
+test_expect_success 'with succeeding hook' '
+
+       echo "more" >> file &&
+       git add file &&
+       git commit -m "more"
+
+'
+
+test_expect_success '--no-verify with succeeding hook' '
+
+       echo "even more" >> file &&
+       git add file &&
+       git commit --no-verify -m "even more"
+
+'
+
+# now a hook that fails
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_failure 'with failing hook' '
+
+       echo "another" >> file &&
+       git add file &&
+       git commit -m "another"
+
+'
+
+test_expect_success '--no-verify with failing hook' '
+
+       echo "stuff" >> file &&
+       git add file &&
+       git commit --no-verify -m "stuff"
+
+'
+
+chmod -x "$HOOK"
+test_expect_success 'with non-executable hook' '
+
+       echo "content" >> file &&
+       git add file &&
+       git commit -m "content"
+
+'
+
+test_expect_success '--no-verify with non-executable hook' '
+
+       echo "more content" >> file &&
+       git add file &&
+       git commit --no-verify -m "more content"
+
+'
+
+test_done
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
new file mode 100755 (executable)
index 0000000..751b113
--- /dev/null
@@ -0,0 +1,220 @@
+#!/bin/sh
+
+test_description='commit-msg hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+       echo "foo" > file &&
+       git add file &&
+       git commit -m "first"
+
+'
+
+# set up fake editor for interactive editing
+cat > fake-editor <<'EOF'
+#!/bin/sh
+cp FAKE_MSG "$1"
+exit 0
+EOF
+chmod +x fake-editor
+FAKE_EDITOR="$(pwd)/fake-editor"
+export FAKE_EDITOR
+
+test_expect_success 'with no hook (editor)' '
+
+       echo "more foo" >> file &&
+       git add file &&
+       echo "more foo" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit
+
+'
+
+test_expect_success '--no-verify with no hook' '
+
+       echo "bar" > file &&
+       git add file &&
+       git commit --no-verify -m "bar"
+
+'
+
+test_expect_success '--no-verify with no hook (editor)' '
+
+       echo "more bar" > file &&
+       git add file &&
+       echo "more bar" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+
+'
+
+# now install hook that always succeeds
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/commit-msg"
+mkdir -p "$HOOKDIR"
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 0
+EOF
+chmod +x "$HOOK"
+
+test_expect_success 'with succeeding hook' '
+
+       echo "more" >> file &&
+       git add file &&
+       git commit -m "more"
+
+'
+
+test_expect_success 'with succeeding hook (editor)' '
+
+       echo "more more" >> file &&
+       git add file &&
+       echo "more more" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit
+
+'
+
+test_expect_success '--no-verify with succeeding hook' '
+
+       echo "even more" >> file &&
+       git add file &&
+       git commit --no-verify -m "even more"
+
+'
+
+test_expect_success '--no-verify with succeeding hook (editor)' '
+
+       echo "even more more" >> file &&
+       git add file &&
+       echo "even more more" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+
+'
+
+# now a hook that fails
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_failure 'with failing hook' '
+
+       echo "another" >> file &&
+       git add file &&
+       git commit -m "another"
+
+'
+
+test_expect_failure 'with failing hook (editor)' '
+
+       echo "more another" >> file &&
+       git add file &&
+       echo "more another" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit
+
+'
+
+test_expect_success '--no-verify with failing hook' '
+
+       echo "stuff" >> file &&
+       git add file &&
+       git commit --no-verify -m "stuff"
+
+'
+
+test_expect_success '--no-verify with failing hook (editor)' '
+
+       echo "more stuff" >> file &&
+       git add file &&
+       echo "more stuff" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+
+'
+
+chmod -x "$HOOK"
+test_expect_success 'with non-executable hook' '
+
+       echo "content" >> file &&
+       git add file &&
+       git commit -m "content"
+
+'
+
+test_expect_success 'with non-executable hook (editor)' '
+
+       echo "content again" >> file &&
+       git add file &&
+       echo "content again" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit -m "content again"
+
+'
+
+test_expect_success '--no-verify with non-executable hook' '
+
+       echo "more content" >> file &&
+       git add file &&
+       git commit --no-verify -m "more content"
+
+'
+
+test_expect_success '--no-verify with non-executable hook (editor)' '
+
+       echo "even more content" >> file &&
+       git add file &&
+       echo "even more content" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+
+'
+
+# now a hook that edits the commit message
+cat > "$HOOK" <<'EOF'
+#!/bin/sh
+echo "new message" > "$1"
+exit 0
+EOF
+chmod +x "$HOOK"
+
+commit_msg_is () {
+       test "`git log --pretty=format:%s%b -1`" = "$1"
+}
+
+test_expect_success 'hook edits commit message' '
+
+       echo "additional" >> file &&
+       git add file &&
+       git commit -m "additional" &&
+       commit_msg_is "new message"
+
+'
+
+test_expect_success 'hook edits commit message (editor)' '
+
+       echo "additional content" >> file &&
+       git add file &&
+       echo "additional content" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit &&
+       commit_msg_is "new message"
+
+'
+
+test_expect_success "hook doesn't edit commit message" '
+
+       echo "plus" >> file &&
+       git add file &&
+       git commit --no-verify -m "plus" &&
+       commit_msg_is "plus"
+
+'
+
+test_expect_success "hook doesn't edit commit message (editor)" '
+
+       echo "more plus" >> file &&
+       git add file &&
+       echo "more plus" > FAKE_MSG &&
+       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify &&
+       commit_msg_is "more plus"
+
+'
+
+test_done
index 67fdf7023f3aed9273eeaf5664cbd62950e32616..0a41d52c7a734c530cd93e8f536aa9a0a8b5a3f6 100755 (executable)
@@ -97,15 +97,19 @@ test_expect_success 'migrate --minimize on old inited layout' "
        grep '^:refs/remotes/git-svn' fetch.out
        "
 
-test_expect_success  ".rev_db auto-converted to .rev_db.UUID" "
+test_expect_success  ".rev_db auto-converted to .rev_map.UUID" "
        git-svn fetch -i trunk &&
-       expect=$GIT_DIR/svn/trunk/.rev_db.* &&
+       test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
+       expect=\"\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\" &&
        test -n \"\$expect\" &&
-       mv \$expect $GIT_DIR/svn/trunk/.rev_db &&
+       rev_db=\$(echo \$expect | sed -e 's,_map,_db,') &&
+       convert_to_rev_db \$expect \$rev_db &&
+       rm -f \$expect &&
+       test -f \$rev_db &&
        git-svn fetch -i trunk &&
-       test -L $GIT_DIR/svn/trunk/.rev_db &&
-       test -f \$expect &&
-       cmp \$expect $GIT_DIR/svn/trunk/.rev_db
+       test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
+       test ! -e $GIT_DIR/svn/trunk/.rev_db &&
+       test -f \$expect
        "
 
 test_done
index 439bd93c886091f820a7de48a0fe92966e31fe1c..cc619115931cb74a85a171ade915ca2c47639c9b 100755 (executable)
@@ -5,6 +5,8 @@
 test_description='git-svn info'
 
 . ./lib-git-svn.sh
+say 'skipping svn-info test (has a race undiagnosed yet)'
+test_done
 
 ptouch() {
        perl -w -e '
index aa0a100295c5c48483c6bfbbfc0105b70680becc..e1e2e6c6ce3c4effffb26e9aed7c6bdd2b00a5ae 100644 (file)
@@ -326,6 +326,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                die("unable to set up diff options to follow renames");
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
+       diff_tree_release_paths(&diff_opts);
 
        /* Go through the new set of filepairing, and see if we find a more interesting one */
        for (i = 0; i < q->nr; i++) {
@@ -342,6 +343,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                        choice = p;
 
                        /* Update the path we use from now on.. */
+                       diff_tree_release_paths(opt);
                        opt->paths[0] = xstrdup(p->one->path);
                        diff_tree_setup_paths(opt->paths, opt);
                        break;
diff --git a/var.c b/var.c
index 4127031910c83987c8ef394e3711942e2db8a8f9..0de0efa2aa3b216a0bc846135a54ed0dd0549f8b 100644 (file)
--- a/var.c
+++ b/var.c
@@ -21,7 +21,7 @@ static void list_vars(void)
 {
        struct git_var *ptr;
        for(ptr = git_vars; ptr->read; ptr++) {
-               printf("%s=%s\n", ptr->name, ptr->read(0));
+               printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
        }
 }
 
@@ -32,7 +32,7 @@ static const char *read_var(const char *var)
        val = NULL;
        for(ptr = git_vars; ptr->read; ptr++) {
                if (strcmp(var, ptr->name) == 0) {
-                       val = ptr->read(1);
+                       val = ptr->read(IDENT_ERROR_ON_NO_NAME);
                        break;
                }
        }
diff --git a/ws.c b/ws.c
new file mode 100644 (file)
index 0000000..52c10ca
--- /dev/null
+++ b/ws.c
@@ -0,0 +1,96 @@
+/*
+ * Whitespace rules
+ *
+ * Copyright (c) 2007 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "attr.h"
+
+static struct whitespace_rule {
+       const char *rule_name;
+       unsigned rule_bits;
+} whitespace_rule_names[] = {
+       { "trailing-space", WS_TRAILING_SPACE },
+       { "space-before-tab", WS_SPACE_BEFORE_TAB },
+       { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
+};
+
+unsigned parse_whitespace_rule(const char *string)
+{
+       unsigned rule = WS_DEFAULT_RULE;
+
+       while (string) {
+               int i;
+               size_t len;
+               const char *ep;
+               int negated = 0;
+
+               string = string + strspn(string, ", \t\n\r");
+               ep = strchr(string, ',');
+               if (!ep)
+                       len = strlen(string);
+               else
+                       len = ep - string;
+
+               if (*string == '-') {
+                       negated = 1;
+                       string++;
+                       len--;
+               }
+               if (!len)
+                       break;
+               for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {
+                       if (strncmp(whitespace_rule_names[i].rule_name,
+                                   string, len))
+                               continue;
+                       if (negated)
+                               rule &= ~whitespace_rule_names[i].rule_bits;
+                       else
+                               rule |= whitespace_rule_names[i].rule_bits;
+                       break;
+               }
+               string = ep;
+       }
+       return rule;
+}
+
+static void setup_whitespace_attr_check(struct git_attr_check *check)
+{
+       static struct git_attr *attr_whitespace;
+
+       if (!attr_whitespace)
+               attr_whitespace = git_attr("whitespace", 10);
+       check[0].attr = attr_whitespace;
+}
+
+unsigned whitespace_rule(const char *pathname)
+{
+       struct git_attr_check attr_whitespace_rule;
+
+       setup_whitespace_attr_check(&attr_whitespace_rule);
+       if (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {
+               const char *value;
+
+               value = attr_whitespace_rule.value;
+               if (ATTR_TRUE(value)) {
+                       /* true (whitespace) */
+                       unsigned all_rule = 0;
+                       int i;
+                       for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
+                               all_rule |= whitespace_rule_names[i].rule_bits;
+                       return all_rule;
+               } else if (ATTR_FALSE(value)) {
+                       /* false (-whitespace) */
+                       return 0;
+               } else if (ATTR_UNSET(value)) {
+                       /* reset to default (!whitespace) */
+                       return whitespace_rule_cfg;
+               } else {
+                       /* string */
+                       return parse_whitespace_rule(value);
+               }
+       } else {
+               return whitespace_rule_cfg;
+       }
+}