Merge branch 'tr/perftest'
authorJunio C Hamano <gitster@pobox.com>
Thu, 23 Feb 2012 21:29:56 +0000 (13:29 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 23 Feb 2012 21:29:56 +0000 (13:29 -0800)
* tr/perftest:
Add a performance test for git-grep
Introduce a performance testing framework
Move the user-facing test library to test-lib-functions.sh

34 files changed:
Documentation/RelNotes/1.7.10.txt
Documentation/RelNotes/1.7.9.2.txt [new file with mode: 0644]
Documentation/git-fmt-merge-msg.txt
Documentation/git-tag.txt
Documentation/git.txt
README
builtin/blame.c
builtin/clone.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/merge.c
builtin/push.c
builtin/receive-pack.c
builtin/rev-list.c
builtin/send-pack.c
builtin/tag.c
cache.h
contrib/completion/git-completion.bash
contrib/diff-highlight/README
contrib/diff-highlight/diff-highlight
contrib/fast-import/git-p4
diff.c
git-svn.perl
gitweb/gitweb.perl
help.c
pager.c
t/t1450-fsck.sh
t/t5504-fetch-receive-strict.sh
t/t5523-push-upstream.sh
t/t5541-http-push.sh
t/t7004-tag.sh
t/t9100-git-svn-basic.sh
t/t9500-gitweb-standalone-no-errors.sh
transport.c
index be3001afe239121328b9d949332e4bf96698ec67..4ea69322bf89da922605aab04daf5785efa12b9e 100644 (file)
@@ -8,8 +8,6 @@ UI, Workflows & Features
 
  * Improved handling of views, labels and branches in git-p4 (in contrib).
 
- * Updated command line arguments completion script for zsh (in contrib).
-
  * "vcs-svn"/"svn-fe" learned to read dumps with svn-deltas and
    support incremental imports.
 
@@ -22,19 +20,22 @@ UI, Workflows & Features
  * "git clone" learned to detach the HEAD in the resulting repository
    when the source repository's HEAD does not point to a branch.
 
- * The commands in the "git diff" family and "git apply --stat" that
-   count the number of files changed and the number of lines
-   inserted/deleted have been updated to match the output from
-   "diffstat".  This also opens the door to i18n this line.
-
  * When showing a patch while ignoring whitespace changes, the context
    lines are taken from the postimage, in order to make it easier to
    view the output.
 
+ * "diff-highlight" filter (in contrib/) was updated to produce more
+   aesthetically pleasing output.
+
+ * "git tag --list" can be given "--points-at <object>" to limit its
+   output to those that point at the given object.
+
  * "git merge" in an interactive session learned to spawn the editor
    by default to let the user edit the auto-generated merge message,
    to encourage people to explain their merges better. Legacy scripts
    can export MERGE_AUTOEDIT=no to retain the historical behaviour.
+   Both "git merge" and "git pull" can be given --no-edit from the
+   command line to accept the auto-generated merge message.
 
  * "gitweb" allows intermediate entries in the directory hierarchy
    that leads to a projects to be clicked, which in turn shows the
@@ -46,13 +47,16 @@ Performance
    to parse_object() have been eliminated, to help performance in
    repositories with excessive number of refs.
 
-Internal Implementation
+Internal Implementation (please report possible regressions)
 
  * Recursive call chains in "git index-pack" to deal with long delta
    chains have been flattened, to reduce the stack footprint.
 
- * Use of add_extra_ref() API is slowly getting removed, to make it
-   possible to cleanly restructure the overall refs API.
+ * Use of add_extra_ref() API is now gone, to make it possible to
+   cleanly restructure the overall refs API.
+
+ * The command line parser of "git pack-objects" now uses parse-options
+   API.
 
  * The test suite supports the new "test_pause" helper function.
 
@@ -69,35 +73,13 @@ Unless otherwise noted, all the fixes since v1.7.9 in the maintenance
 releases are contained in this release (see release notes to them for
 details).
 
- * The error message emitted when we see an empty loose object was
-   not phrased correctly.
-   (merge 33e42de mm/empty-loose-error-message later to maint).
-
- * "git commit" refused to create a commit when entries added with
-   "add -N" remained in the index, without telling Git what their content
-   in the next commit should be. We should have created the commit without
-   these paths.
-   (merge 3f6d56d jc/maint-commit-ignore-i-t-a later to maint).
-
- * Search box in "gitweb" did not accept non-ASCII characters correctly.
-   (merge 84d9e2d jn/gitweb-search-utf-8 later to maint).
-
- * The code to ask for password did not fall back to the terminal
-   input when GIT_ASKPASS is set but does not work (e.g. lack of X
-   with GUI askpass helper).
-   (merge 84d7273 jk/prompt-fallback-to-tty later to maint).
-
- * map_user() was not rewriting its output correctly, which resulted
-   in the user visible symptom that "git blame -e" sometimes showed
-   excess '>' at the end of email addresses.
-   (merge f026358 jc/maint-mailmap-output later to maint).
-
- * "checkout -b" did not allow switching out of an unborn branch.
-   (merge abe1998 jc/checkout-out-of-unborn later to maint).
+ * "gitweb" used to drop warnings in the log file when "heads" view is
+   accessed in a repository whose HEAD does not point at a valid
+   branch.
 
 ---
 exec >/var/tmp/1
-O=v1.7.9-249-gaa47ec9
+O=v1.7.9.1-289-g5609586
 echo O=$(git describe)
 git log --first-parent --oneline ^maint $O..
 echo
diff --git a/Documentation/RelNotes/1.7.9.2.txt b/Documentation/RelNotes/1.7.9.2.txt
new file mode 100644 (file)
index 0000000..e500da7
--- /dev/null
@@ -0,0 +1,69 @@
+Git v1.7.9.2 Release Notes
+==========================
+
+Fixes since v1.7.9.1
+--------------------
+
+ * Bash completion script (in contrib/) did not like a pattern that
+   begins with a dash to be passed to __git_ps1 helper function.
+
+ * Adaptation of the bash completion script (in contrib/) for zsh
+   incorrectly listed all subcommands when "git <TAB><TAB>" was given
+   to ask for list of porcelain subcommands.
+
+ * The build procedure for profile-directed optimized binary was not
+   working very well.
+
+ * Some systems need to explicitly link -lcharset to get locale_charset().
+
+ * t5541 ignored user-supplied port number used for HTTP server testing.
+
+ * The error message emitted when we see an empty loose object was
+   not phrased correctly.
+
+ * The code to ask for password did not fall back to the terminal
+   input when GIT_ASKPASS is set but does not work (e.g. lack of X
+   with GUI askpass helper).
+
+ * We failed to give the true terminal width to any subcommand when
+   they are invoked with the pager, i.e. "git -p cmd".
+
+ * map_user() was not rewriting its output correctly, which resulted
+   in the user visible symptom that "git blame -e" sometimes showed
+   excess '>' at the end of email addresses.
+
+ * "git checkout -b" did not allow switching out of an unborn branch.
+
+ * When you have both .../foo and .../foo.git, "git clone .../foo" did not
+   favor the former but the latter.
+
+ * "git commit" refused to create a commit when entries added with
+   "add -N" remained in the index, without telling Git what their content
+   in the next commit should be. We should have created the commit without
+   these paths.
+
+ * "git diff --stat" said "files", "insertions", and "deletions" even
+   when it is showing one "file", one "insertion" or one "deletion".
+
+ * The output from "git diff --stat" for two paths that have the same
+   amount of changes showed graph bars of different length due to the
+   way we handled rounding errors.
+
+ * "git grep" did not pay attention to -diff (hence -binary) attribute.
+
+ * The transport programs (fetch, push, clone)ignored --no-progress
+   and showed progress when sending their output to a terminal.
+
+ * Sometimes error status detected by a check in an earlier phase of
+   "git receive-pack" (the other end of "git push") was lost by later
+   checks, resulting in false indication of success.
+
+ * "git rev-list --verify" sometimes skipped verification depending on
+   the phase of the moon, which dates back to 1.7.8.x series.
+
+ * Search box in "gitweb" did not accept non-ASCII characters correctly.
+
+ * Search interface of "gitweb" did not show multiple matches in the same file
+   correctly.
+
+Also contains minor fixes and documentation updates.
index 32aff954a2b2f95a61b39b8d08fb0482724400bb..3a0f55ec8e273545af3a92a9b886511ac79ba73c 100644 (file)
@@ -53,6 +53,11 @@ OPTIONS
 CONFIGURATION
 -------------
 
+merge.branchdesc::
+       In addition to branch names, populate the log message with
+       the branch description text associated with them.  Defaults
+       to false.
+
 merge.log::
        In addition to branch names, populate the log message with at
        most the specified number of one-line descriptions from the
index 53ff5f6cf7b9420933b022accace1355db6337c6..8d32b9a814675c9ebb58c25615a7f647dae20c93 100644 (file)
@@ -12,7 +12,8 @@ SYNOPSIS
 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>...]
+'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
+       [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -86,6 +87,9 @@ OPTIONS
 --contains <commit>::
        Only list tags which contain the specified commit.
 
+--points-at <object>::
+       Only list tags of the given object.
+
 -m <msg>::
 --message=<msg>::
        Use the given tag message (instead of prompting).
index a12d15fb98eae16c1b88e8e46ca65fe1af9c1dfb..22fadeb114b56249920a3338a6ceb7bd71615a79 100644 (file)
@@ -9,11 +9,11 @@ git - the stupid content tracker
 SYNOPSIS
 --------
 [verse]
-'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
+'git' [--version] [--help] [-c <name>=<value>]
+    [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
     [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
     [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
-    [-c <name>=<value>]
-    [--help] <command> [<args>]
+    <command> [<args>]
 
 DESCRIPTION
 -----------
@@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.9.1/git.html[documentation for release 1.7.9.1]
+* link:v1.7.9.2/git.html[documentation for release 1.7.9.2]
 
 * release notes for
+  link:RelNotes/1.7.9.2.txt[1.7.9.2],
   link:RelNotes/1.7.9.1.txt[1.7.9.1],
   link:RelNotes/1.7.9.txt[1.7.9].
 
diff --git a/README b/README
index 67cfeb2016b24df1cb406c18145efd399f6a1792..d2690ec8dc6e5f054c33a5f3b4c26f6ad038706d 100644 (file)
--- a/README
+++ b/README
@@ -42,10 +42,12 @@ including full documentation and Git related tools.
 
 The user discussion and development of Git take place on the Git
 mailing list -- everyone is welcome to post bug reports, feature
-requests, comments and patches to git@vger.kernel.org. To subscribe
-to the list, send an email with just "subscribe git" in the body to
-majordomo@vger.kernel.org. The mailing list archives are available at
-http://marc.theaimsgroup.com/?l=git and other archival sites.
+requests, comments and patches to git@vger.kernel.org (read
+Documentation/SubmittingPatches for instructions on patch submission).
+To subscribe to the list, send an email with just "subscribe git" in
+the body to majordomo@vger.kernel.org. The mailing list archives are
+available at http://marc.theaimsgroup.com/?l=git and other archival
+sites.
 
 The messages titled "A note from the maintainer", "What's in
 git.git (stable)" and "What's cooking in git.git (topics)" and
index 01956c80815a4f330ae27b77b4d21d263a887d1b..b35bd6249de66d02b7f33eb7aae4866193447156 100644 (file)
@@ -1828,18 +1828,6 @@ static int read_ancestry(const char *graft_file)
        return 0;
 }
 
-/*
- * How many columns do we need to show line numbers in decimal?
- */
-static int lineno_width(int lines)
-{
-       int i, width;
-
-       for (width = 1, i = 10; i <= lines; width++)
-               i *= 10;
-       return width;
-}
-
 /*
  * How many columns do we need to show line numbers, authors,
  * and filenames?
@@ -1880,9 +1868,9 @@ static void find_alignment(struct scoreboard *sb, int *option)
                if (largest_score < ent_score(sb, e))
                        largest_score = ent_score(sb, e);
        }
-       max_orig_digits = lineno_width(longest_src_lines);
-       max_digits = lineno_width(longest_dst_lines);
-       max_score_digits = lineno_width(largest_score);
+       max_orig_digits = decimal_width(longest_src_lines);
+       max_digits = decimal_width(longest_dst_lines);
+       max_score_digits = decimal_width(largest_score);
 }
 
 /*
index 7559f62bc7a846ecb8f8d085e8bf5c3e6d4df2cd..bbd5c96237fc332e159face6c8678d8ae3b9a3e9 100644 (file)
@@ -45,7 +45,7 @@ static char *option_branch = NULL;
 static const char *real_git_dir;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
-static int option_progress;
+static int option_progress = -1;
 static struct string_list option_config;
 static struct string_list option_reference;
 
@@ -60,8 +60,8 @@ static int opt_parse_reference(const struct option *opt, const char *arg, int un
 
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
-       OPT_BOOLEAN(0, "progress", &option_progress,
-                       "force progress reporting"),
+       OPT_BOOL(0, "progress", &option_progress,
+                "force progress reporting"),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
                    "don't create a checkout"),
        OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
index 0e8560f60f43dd6e971a38381838d217bdc28899..7124c4b49cfba7985c5ba2046296f006de04e3bb 100644 (file)
@@ -737,7 +737,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
        }
        else {
                *av++ = "unpack-objects";
-               if (args.quiet)
+               if (args.quiet || args.no_progress)
                        *av++ = "-q";
        }
        if (*hdr_arg)
index ab186332fa881e0f823b0042f2d650fda8f365a8..65f5f9b72f92ec64ac5ad1ad264f78199337aba6 100644 (file)
@@ -30,7 +30,7 @@ enum {
 };
 
 static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
-static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -78,7 +78,7 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
-       OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
+       OPT_BOOL(0, "progress", &progress, "force progress reporting"),
        OPT_STRING(0, "depth", &depth, "depth",
                   "deepen history of shallow clone"),
        { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
index ed0f959ac49c02aaffa77bc748a9efc69f1fd339..d3e1e8dc9e478aaea7ef4da855fb7c2e10397644 100644 (file)
@@ -1129,7 +1129,7 @@ static int default_edit_option(void)
        /* Use editor if stdin and stdout are the same and is a tty */
        return (!fstat(0, &st_stdin) &&
                !fstat(1, &st_stdout) &&
-               isatty(0) &&
+               isatty(0) && isatty(1) &&
                st_stdin.st_dev == st_stdout.st_dev &&
                st_stdin.st_ino == st_stdout.st_ino &&
                st_stdin.st_mode == st_stdout.st_mode);
index 35cce532f2bb632e01c0de0a8e6f9e1395eece88..6c373cf28bfc9d8409014d84eef5b886218b25ab 100644 (file)
@@ -19,7 +19,7 @@ static int thin;
 static int deleterefs;
 static const char *receivepack;
 static int verbosity;
-static int progress;
+static int progress = -1;
 
 static const char **refspec;
 static int refspec_nr;
@@ -260,7 +260,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
                OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
                        TRANSPORT_PUSH_SET_UPSTREAM),
-               OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
+               OPT_BOOL(0, "progress", &progress, "force progress reporting"),
                OPT_END()
        };
 
index fa7448be5aaf9d2830168b77f9a555e9b6740993..0afb8b289621c419bd7472097335e4235da37d61 100644 (file)
@@ -642,8 +642,10 @@ static void check_aliased_updates(struct command *commands)
        }
        sort_string_list(&ref_list);
 
-       for (cmd = commands; cmd; cmd = cmd->next)
-               check_aliased_update(cmd, &ref_list);
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               if (!cmd->error_string)
+                       check_aliased_update(cmd, &ref_list);
+       }
 
        string_list_clear(&ref_list, 0);
 }
@@ -707,8 +709,10 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
                set_connectivity_errors(commands);
 
        if (run_receive_hook(commands, pre_receive_hook, 0)) {
-               for (cmd = commands; cmd; cmd = cmd->next)
-                       cmd->error_string = "pre-receive hook declined";
+               for (cmd = commands; cmd; cmd = cmd->next) {
+                       if (!cmd->error_string)
+                               cmd->error_string = "pre-receive hook declined";
+               }
                return;
        }
 
@@ -717,9 +721,15 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
        free(head_name_to_free);
        head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
 
-       for (cmd = commands; cmd; cmd = cmd->next)
-               if (!cmd->skip_update)
-                       cmd->error_string = update(cmd);
+       for (cmd = commands; cmd; cmd = cmd->next) {
+               if (cmd->error_string)
+                       continue;
+
+               if (cmd->skip_update)
+                       continue;
+
+               cmd->error_string = update(cmd);
+       }
 }
 
 static struct command *read_head_info(void)
index ab3be7ca82ea36fbb1e98f8b899970a6748981c6..264e3ae9d840c342499634e21b21c274ebcebacf 100644 (file)
@@ -180,10 +180,10 @@ static void show_object(struct object *obj,
                        const struct name_path *path, const char *component,
                        void *cb_data)
 {
-       struct rev_info *info = cb_data;
+       struct rev_list_info *info = cb_data;
 
        finish_object(obj, path, component, cb_data);
-       if (info->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
+       if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
                parse_object(obj->sha1);
        show_object_with_name(stdout, obj, path, component);
 }
index 71f258ef6e620979a9dbeaa87d2501a304b3a0fa..9df341c793d58eff215805bf5ca5da383f4a99d9 100644 (file)
@@ -58,7 +58,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
                argv[i++] = "--thin";
        if (args->use_ofs_delta)
                argv[i++] = "--delta-base-offset";
-       if (args->quiet)
+       if (args->quiet || !args->progress)
                argv[i++] = "-q";
        if (args->progress)
                argv[i++] = "--progress";
@@ -250,6 +250,7 @@ int send_pack(struct send_pack_args *args,
        int allow_deleting_refs = 0;
        int status_report = 0;
        int use_sideband = 0;
+       int quiet_supported = 0;
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
@@ -263,8 +264,8 @@ int send_pack(struct send_pack_args *args,
                args->use_ofs_delta = 1;
        if (server_supports("side-band-64k"))
                use_sideband = 1;
-       if (!server_supports("quiet"))
-               args->quiet = 0;
+       if (server_supports("quiet"))
+               quiet_supported = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -302,17 +303,18 @@ int send_pack(struct send_pack_args *args,
                } else {
                        char *old_hex = sha1_to_hex(ref->old_sha1);
                        char *new_hex = sha1_to_hex(ref->new_sha1);
+                       int quiet = quiet_supported && (args->quiet || !args->progress);
 
                        if (!cmds_sent && (status_report || use_sideband || args->quiet)) {
                                packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
-                                       old_hex, new_hex, ref->name, 0,
-                                       status_report ? " report-status" : "",
-                                       use_sideband ? " side-band-64k" : "",
-                                       args->quiet ? " quiet" : "");
+                                                old_hex, new_hex, ref->name, 0,
+                                                status_report ? " report-status" : "",
+                                                use_sideband ? " side-band-64k" : "",
+                                                quiet ? " quiet" : "");
                        }
                        else
                                packet_buf_write(&req_buf, "%s %s %s",
-                                       old_hex, new_hex, ref->name);
+                                                old_hex, new_hex, ref->name);
                        ref->status = status_report ?
                                REF_STATUS_EXPECTING_REPORT :
                                REF_STATUS_OK;
index 03df16ac6e0e492483bf3695e0f6b2aa0a95978b..fe7e5e5b3d64dc8168ebe687ac5c6c2d1cbbdba1 100644 (file)
 #include "diff.h"
 #include "revision.h"
 #include "gpg-interface.h"
+#include "sha1-array.h"
 
 static const char * const git_tag_usage[] = {
        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
        "git tag -d <tagname>...",
-       "git tag -l [-n[<num>]] [<pattern>...]",
+       "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
+               "\n\t\t[<pattern>...]",
        "git tag -v <tagname>...",
        NULL
 };
@@ -30,6 +32,8 @@ struct tag_filter {
        struct commit_list *with_commit;
 };
 
+static struct sha1_array points_at;
+
 static int match_pattern(const char **patterns, const char *ref)
 {
        /* no pattern means match everything */
@@ -41,6 +45,24 @@ static int match_pattern(const char **patterns, const char *ref)
        return 0;
 }
 
+static const unsigned char *match_points_at(const char *refname,
+                                           const unsigned char *sha1)
+{
+       const unsigned char *tagged_sha1 = NULL;
+       struct object *obj;
+
+       if (sha1_array_lookup(&points_at, sha1) >= 0)
+               return sha1;
+       obj = parse_object(sha1);
+       if (!obj)
+               die(_("malformed object at '%s'"), refname);
+       if (obj->type == OBJ_TAG)
+               tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+       if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
+               return tagged_sha1;
+       return NULL;
+}
+
 static int in_commit_list(const struct commit_list *want, struct commit *c)
 {
        for (; want; want = want->next)
@@ -138,6 +160,9 @@ static int show_reference(const char *refname, const unsigned char *sha1,
                                return 0;
                }
 
+               if (points_at.nr && !match_points_at(refname, sha1))
+                       return 0;
+
                if (!filter->lines) {
                        printf("%s\n", refname);
                        return 0;
@@ -383,6 +408,23 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
        return check_refname_format(sb->buf, 0);
 }
 
+static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
+                       const char *arg, int unset)
+{
+       unsigned char sha1[20];
+
+       if (unset) {
+               sha1_array_clear(&points_at);
+               return 0;
+       }
+       if (!arg)
+               return error(_("switch 'points-at' requires an object"));
+       if (get_sha1(arg, sha1))
+               return error(_("malformed object name '%s'"), arg);
+       sha1_array_append(&points_at, sha1);
+       return 0;
+}
+
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -425,6 +467,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_with_commit, (intptr_t)"HEAD",
                },
+               {
+                       OPTION_CALLBACK, 0, "points-at", NULL, "object",
+                       "print only tags of the object", 0, parse_opt_points_at
+               },
                OPT_END()
        };
 
@@ -456,6 +502,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die(_("-n option is only allowed with -l."));
        if (with_commit)
                die(_("--contains option is only allowed with -l."));
+       if (points_at.nr)
+               die(_("--points-at option is only allowed with -l."));
        if (delete)
                return for_each_tag_name(argv, delete_tag);
        if (verify)
diff --git a/cache.h b/cache.h
index ae0396b84d566108a6b43dfd3ffed3a82f07d2a5..422c5cfcb374259b46a7f92fc2a08b726f54f77a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1177,6 +1177,8 @@ extern void setup_pager(void);
 extern const char *pager_program;
 extern int pager_in_use(void);
 extern int pager_use_color;
+extern int term_columns(void);
+extern int decimal_width(int);
 
 extern const char *editor_program;
 extern const char *askpass_program;
index d7367e9faa87482f638ae4e16df38aa1bb853c40..554e30e961b1459817442c2eb11166c768fa0ec2 100755 (executable)
 #       per-repository basis by setting the bash.showUpstream config
 #       variable.
 #
-#
-# To submit patches:
-#
-#    *) Read Documentation/SubmittingPatches
-#    *) Send all patches to the current maintainer:
-#
-#       "Shawn O. Pearce" <spearce@spearce.org>
-#
-#    *) Always CC the Git mailing list:
-#
-#       git@vger.kernel.org
-#
 
 if [[ -n ${ZSH_VERSION-} ]]; then
        autoload -U +X bashcompinit && bashcompinit
@@ -298,13 +286,13 @@ __git_ps1 ()
                                fi
                        fi
                        if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
                        fi
 
                        if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
-                          if [ -n "$(git ls-files --others --exclude-standard)" ]; then
-                             u="%"
-                          fi
+                               if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+                                       u="%"
+                               fi
                        fi
 
                        if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
@@ -313,7 +301,7 @@ __git_ps1 ()
                fi
 
                local f="$w$i$s$u"
-               printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
+               printf -- "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r$p"
        fi
 }
 
@@ -1137,7 +1125,7 @@ _git_branch ()
                __gitcomp "
                        --color --no-color --verbose --abbrev= --no-abbrev
                        --track --no-track --contains --merged --no-merged
-                       --set-upstream --edit-description
+                       --set-upstream --edit-description --list
                        "
                ;;
        *)
@@ -2512,7 +2500,7 @@ _git_svn ()
                        __gitcomp "
                                --merge --strategy= --verbose --dry-run
                                --fetch-all --no-rebase --commit-url
-                               --revision $cmt_opts $fc_opts
+                               --revision --interactive $cmt_opts $fc_opts
                                "
                        ;;
                set-tree,--*)
index 1b7b6df8ebbdfccab8a632d883c321b4ee2919a4..502e03b3058e947835d396540af39c83a45d42aa 100644 (file)
@@ -14,13 +14,15 @@ Instead, this script post-processes the line-oriented diff, finds pairs
 of lines, and highlights the differing segments.  It's currently very
 simple and stupid about doing these tasks. In particular:
 
-  1. It will only highlight a pair of lines if they are the only two
-     lines in a hunk.  It could instead try to match up "before" and
-     "after" lines for a given hunk into pairs of similar lines.
-     However, this may end up visually distracting, as the paired
-     lines would have other highlighted lines in between them. And in
-     practice, the lines which most need attention called to their
-     small, hard-to-see changes are touching only a single line.
+  1. It will only highlight hunks in which the number of removed and
+     added lines is the same, and it will pair lines within the hunk by
+     position (so the first removed line is compared to the first added
+     line, and so forth). This is simple and tends to work well in
+     practice. More complex changes don't highlight well, so we tend to
+     exclude them due to the "same number of removed and added lines"
+     restriction. Or even if we do try to highlight them, they end up
+     not highlighting because of our "don't highlight if the whole line
+     would be highlighted" rule.
 
   2. It will find the common prefix and suffix of two lines, and
      consider everything in the middle to be "different". It could
@@ -55,3 +57,96 @@ following in your git configuration:
        show = diff-highlight | less
        diff = diff-highlight | less
 ---------------------------------------------
+
+Bugs
+----
+
+Because diff-highlight relies on heuristics to guess which parts of
+changes are important, there are some cases where the highlighting is
+more distracting than useful. Fortunately, these cases are rare in
+practice, and when they do occur, the worst case is simply a little
+extra highlighting. This section documents some cases known to be
+sub-optimal, in case somebody feels like working on improving the
+heuristics.
+
+1. Two changes on the same line get highlighted in a blob. For example,
+   highlighting:
+
+----------------------------------------------
+-foo(buf, size);
++foo(obj->buf, obj->size);
+----------------------------------------------
+
+   yields (where the inside of "+{}" would be highlighted):
+
+----------------------------------------------
+-foo(buf, size);
++foo(+{obj->buf, obj->}size);
+----------------------------------------------
+
+   whereas a more semantically meaningful output would be:
+
+----------------------------------------------
+-foo(buf, size);
++foo(+{obj->}buf, +{obj->}size);
+----------------------------------------------
+
+   Note that doing this right would probably involve a set of
+   content-specific boundary patterns, similar to word-diff. Otherwise
+   you get junk like:
+
+-----------------------------------------------------
+-this line has some -{i}nt-{ere}sti-{ng} text on it
++this line has some +{fa}nt+{a}sti+{c} text on it
+-----------------------------------------------------
+
+   which is less readable than the current output.
+
+2. The multi-line matching assumes that lines in the pre- and post-image
+   match by position. This is often the case, but can be fooled when a
+   line is removed from the top and a new one added at the bottom (or
+   vice versa). Unless the lines in the middle are also changed, diffs
+   will show this as two hunks, and it will not get highlighted at all
+   (which is good). But if the lines in the middle are changed, the
+   highlighting can be misleading. Here's a pathological case:
+
+-----------------------------------------------------
+-one
+-two
+-three
+-four
++two 2
++three 3
++four 4
++five 5
+-----------------------------------------------------
+
+   which gets highlighted as:
+
+-----------------------------------------------------
+-one
+-t-{wo}
+-three
+-f-{our}
++two 2
++t+{hree 3}
++four 4
++f+{ive 5}
+-----------------------------------------------------
+
+   because it matches "two" to "three 3", and so forth. It would be
+   nicer as:
+
+-----------------------------------------------------
+-one
+-two
+-three
+-four
++two +{2}
++three +{3}
++four +{4}
++five 5
+-----------------------------------------------------
+
+   which would probably involve pre-matching the lines into pairs
+   according to some heuristic.
index d8938982e413a9bf994bd12386121249c888649d..c4404d49c9968608510809309b26e2d08eec8810 100755 (executable)
@@ -1,28 +1,37 @@
 #!/usr/bin/perl
 
+use warnings FATAL => 'all';
+use strict;
+
 # Highlight by reversing foreground and background. You could do
 # other things like bold or underline if you prefer.
 my $HIGHLIGHT   = "\x1b[7m";
 my $UNHIGHLIGHT = "\x1b[27m";
 my $COLOR = qr/\x1b\[[0-9;]*m/;
+my $BORING = qr/$COLOR|\s/;
 
-my @window;
+my @removed;
+my @added;
+my $in_hunk;
 
 while (<>) {
-       # We highlight only single-line changes, so we need
-       # a 4-line window to make a decision on whether
-       # to highlight.
-       push @window, $_;
-       next if @window < 4;
-       if ($window[0] =~ /^$COLOR*(\@| )/ &&
-           $window[1] =~ /^$COLOR*-/ &&
-           $window[2] =~ /^$COLOR*\+/ &&
-           $window[3] !~ /^$COLOR*\+/) {
-               print shift @window;
-               show_pair(shift @window, shift @window);
+       if (!$in_hunk) {
+               print;
+               $in_hunk = /^$COLOR*\@/;
+       }
+       elsif (/^$COLOR*-/) {
+               push @removed, $_;
+       }
+       elsif (/^$COLOR*\+/) {
+               push @added, $_;
        }
        else {
-               print shift @window;
+               show_hunk(\@removed, \@added);
+               @removed = ();
+               @added = ();
+
+               print;
+               $in_hunk = /^$COLOR*[\@ ]/;
        }
 
        # Most of the time there is enough output to keep things streaming,
@@ -38,23 +47,40 @@ while (<>) {
        }
 }
 
-# Special case a single-line hunk at the end of file.
-if (@window == 3 &&
-    $window[0] =~ /^$COLOR*(\@| )/ &&
-    $window[1] =~ /^$COLOR*-/ &&
-    $window[2] =~ /^$COLOR*\+/) {
-       print shift @window;
-       show_pair(shift @window, shift @window);
-}
-
-# And then flush any remaining lines.
-while (@window) {
-       print shift @window;
-}
+# Flush any queued hunk (this can happen when there is no trailing context in
+# the final diff of the input).
+show_hunk(\@removed, \@added);
 
 exit 0;
 
-sub show_pair {
+sub show_hunk {
+       my ($a, $b) = @_;
+
+       # If one side is empty, then there is nothing to compare or highlight.
+       if (!@$a || !@$b) {
+               print @$a, @$b;
+               return;
+       }
+
+       # If we have mismatched numbers of lines on each side, we could try to
+       # be clever and match up similar lines. But for now we are simple and
+       # stupid, and only handle multi-line hunks that remove and add the same
+       # number of lines.
+       if (@$a != @$b) {
+               print @$a, @$b;
+               return;
+       }
+
+       my @queue;
+       for (my $i = 0; $i < @$a; $i++) {
+               my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]);
+               print $rm;
+               push @queue, $add;
+       }
+       print @queue;
+}
+
+sub highlight_pair {
        my @a = split_line(shift);
        my @b = split_line(shift);
 
@@ -101,8 +127,14 @@ sub show_pair {
                }
        }
 
-       print highlight(\@a, $pa, $sa);
-       print highlight(\@b, $pb, $sb);
+       if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
+               return highlight_line(\@a, $pa, $sa),
+                      highlight_line(\@b, $pb, $sb);
+       }
+       else {
+               return join('', @a),
+                      join('', @b);
+       }
 }
 
 sub split_line {
@@ -111,7 +143,7 @@ sub split_line {
               split /($COLOR*)/;
 }
 
-sub highlight {
+sub highlight_line {
        my ($line, $prefix, $suffix) = @_;
 
        return join('',
@@ -122,3 +154,20 @@ sub highlight {
                @{$line}[($suffix+1)..$#$line]
        );
 }
+
+# Pairs are interesting to highlight only if we are going to end up
+# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting
+# is just useless noise. We can detect this by finding either a matching prefix
+# or suffix (disregarding boring bits like whitespace and colorization).
+sub is_pair_interesting {
+       my ($a, $pa, $sa, $b, $pb, $sb) = @_;
+       my $prefix_a = join('', @$a[0..($pa-1)]);
+       my $prefix_b = join('', @$b[0..($pb-1)]);
+       my $suffix_a = join('', @$a[($sa+1)..$#$a]);
+       my $suffix_b = join('', @$b[($sb+1)..$#$b]);
+
+       return $prefix_a !~ /^$COLOR*-$BORING*$/ ||
+              $prefix_b !~ /^$COLOR*\+$BORING*$/ ||
+              $suffix_a !~ /^$BORING*$/ ||
+              $suffix_b !~ /^$BORING*$/;
+}
index a78d9c54931c7fc1f6a693a7f185f08bcb8f6861..d2fd265b1c351b942e8ed3e1aa3f95866b6d965d 100755 (executable)
@@ -38,7 +38,7 @@ def p4_build_cmd(cmd):
 
     host = gitConfig("git-p4.host")
     if len(host) > 0:
-        real_cmd += ["-h", host]
+        real_cmd += ["-H", host]
 
     client = gitConfig("git-p4.client")
     if len(client) > 0:
diff --git a/diff.c b/diff.c
index 3550c18e390cac7a3cce8fcf93074c7178bf1994..a1c06b554b80e433dcb735c8e0adf186b6252ae5 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1273,13 +1273,15 @@ const char mime_boundary_leader[] = "------------";
 
 static int scale_linear(int it, int width, int max_change)
 {
+       if (!it)
+               return 0;
        /*
-        * make sure that at least one '-' is printed if there were deletions,
-        * and likewise for '+'.
+        * make sure that at least one '-' or '+' is printed if
+        * there is any change to this path. The easiest way is to
+        * scale linearly as if the alloted width is one column shorter
+        * than it is, and then add 1 to the result.
         */
-       if (max_change < 2)
-               return it;
-       return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
+       return 1 + (it * (width - 1) / max_change);
 }
 
 static void show_name(FILE *file,
@@ -1495,8 +1497,19 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                dels += del;
 
                if (width <= max_change) {
-                       add = scale_linear(add, width, max_change);
-                       del = scale_linear(del, width, max_change);
+                       int total = add + del;
+
+                       total = scale_linear(add + del, width, max_change);
+                       if (total < 2 && add && del)
+                               /* width >= 2 due to the sanity check */
+                               total = 2;
+                       if (add < del) {
+                               add = scale_linear(add, width, max_change);
+                               del = total - add;
+                       } else {
+                               del = scale_linear(del, width, max_change);
+                               add = total - del;
+                       }
                }
                fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);
index eeb83d375931df81bd87ca285c8d0741c14f33a2..4334b95f70fab5c3cb9e446a9d6c418748b95bca 100755 (executable)
@@ -1878,8 +1878,7 @@ sub cmt_sha2rev_batch {
 
 sub working_head_info {
        my ($head, $refs) = @_;
-       my @args = qw/log --no-color --no-decorate --first-parent
-                     --pretty=medium/;
+       my @args = qw/rev-list --first-parent --pretty=medium/;
        my ($fh, $ctx) = command_output_pipe(@args, $head);
        my $hash;
        my %max;
@@ -2029,6 +2028,7 @@ package Git::SVN;
 use File::Path qw/mkpath/;
 use File::Copy qw/copy/;
 use IPC::Open3;
+use Time::Local;
 use Memoize;  # core since 5.8.0, Jul 2002
 use Memoize::Storable;
 
@@ -3287,6 +3287,14 @@ sub get_untracked {
        \@out;
 }
 
+sub get_tz {
+       # some systmes don't handle or mishandle %z, so be creative.
+       my $t = shift || time;
+       my $gm = timelocal(gmtime($t));
+       my $sign = qw( + + - )[ $t <=> $gm ];
+       return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
+}
+
 # parse_svn_date(DATE)
 # --------------------
 # Given a date (in UTC) from Subversion, return a string in the format
@@ -3319,8 +3327,7 @@ sub parse_svn_date {
                        delete $ENV{TZ};
                }
 
-               my $our_TZ =
-                   POSIX::strftime('%Z', $S, $M, $H, $d, $m - 1, $Y - 1900);
+               my $our_TZ = get_tz();
 
                # This converts $epoch_in_UTC into our local timezone.
                my ($sec, $min, $hour, $mday, $mon, $year,
@@ -3920,7 +3927,7 @@ sub rebuild {
        my ($base_rev, $head) = ($partial ? $self->rev_map_max_norebuild(1) :
                (undef, undef));
        my ($log, $ctx) =
-           command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
+           command_output_pipe(qw/rev-list --pretty=raw --reverse/,
                                ($head ? "$head.." : "") . $self->refname,
                                '--');
        my $metadata_url = $self->metadata_url;
@@ -5130,7 +5137,7 @@ sub rmdirs {
 }
 
 sub open_or_add_dir {
-       my ($self, $full_path, $baton) = @_;
+       my ($self, $full_path, $baton, $deletions) = @_;
        my $t = $self->{types}->{$full_path};
        if (!defined $t) {
                die "$full_path not known in r$self->{r} or we have a bug!\n";
@@ -5139,7 +5146,7 @@ sub open_or_add_dir {
                no warnings 'once';
                # SVN::Node::none and SVN::Node::file are used only once,
                # so we're shutting up Perl's warnings about them.
-               if ($t == $SVN::Node::none) {
+               if ($t == $SVN::Node::none || defined($deletions->{$full_path})) {
                        return $self->add_directory($full_path, $baton,
                            undef, -1, $self->{pool});
                } elsif ($t == $SVN::Node::dir) {
@@ -5154,17 +5161,18 @@ sub open_or_add_dir {
 }
 
 sub ensure_path {
-       my ($self, $path) = @_;
+       my ($self, $path, $deletions) = @_;
        my $bat = $self->{bat};
        my $repo_path = $self->repo_path($path);
        return $bat->{''} unless (length $repo_path);
+
        my @p = split m#/+#, $repo_path;
        my $c = shift @p;
-       $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''});
+       $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}, $deletions);
        while (@p) {
                my $c0 = $c;
                $c .= '/' . shift @p;
-               $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0});
+               $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}, $deletions);
        }
        return $bat->{$c};
 }
@@ -5221,9 +5229,9 @@ sub apply_autoprops {
 }
 
 sub A {
-       my ($self, $m) = @_;
+       my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
-       my $pbat = $self->ensure_path($dir);
+       my $pbat = $self->ensure_path($dir, $deletions);
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
                                        undef, -1);
        print "\tA\t$m->{file_b}\n" unless $::_q;
@@ -5233,9 +5241,9 @@ sub A {
 }
 
 sub C {
-       my ($self, $m) = @_;
+       my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
-       my $pbat = $self->ensure_path($dir);
+       my $pbat = $self->ensure_path($dir, $deletions);
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
                                $self->url_path($m->{file_a}), $self->{r});
        print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
@@ -5252,9 +5260,9 @@ sub delete_entry {
 }
 
 sub R {
-       my ($self, $m) = @_;
+       my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
-       my $pbat = $self->ensure_path($dir);
+       my $pbat = $self->ensure_path($dir, $deletions);
        my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
                                $self->url_path($m->{file_a}), $self->{r});
        print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
@@ -5263,14 +5271,14 @@ sub R {
        $self->close_file($fbat,undef,$self->{pool});
 
        ($dir, $file) = split_path($m->{file_a});
-       $pbat = $self->ensure_path($dir);
+       $pbat = $self->ensure_path($dir, $deletions);
        $self->delete_entry($m->{file_a}, $pbat);
 }
 
 sub M {
-       my ($self, $m) = @_;
+       my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
-       my $pbat = $self->ensure_path($dir);
+       my $pbat = $self->ensure_path($dir, $deletions);
        my $fbat = $self->open_file($self->repo_path($m->{file_b}),
                                $pbat,$self->{r},$self->{pool});
        print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
@@ -5340,9 +5348,9 @@ sub chg_file {
 }
 
 sub D {
-       my ($self, $m) = @_;
+       my ($self, $m, $deletions) = @_;
        my ($dir, $file) = split_path($m->{file_b});
-       my $pbat = $self->ensure_path($dir);
+       my $pbat = $self->ensure_path($dir, $deletions);
        print "\tD\t$m->{file_b}\n" unless $::_q;
        $self->delete_entry($m->{file_b}, $pbat);
 }
@@ -5374,11 +5382,19 @@ sub DESTROY {
 sub apply_diff {
        my ($self) = @_;
        my $mods = $self->{mods};
-       my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
+       my %o = ( D => 0, C => 1, R => 2, A => 3, M => 4, T => 5 );
+       my %deletions;
+
+       foreach my $m (@$mods) {
+               if ($m->{chg} eq "D") {
+                       $deletions{$m->{file_b}} = 1;
+               }
+       }
+
        foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
                my $f = $m->{chg};
                if (defined $o{$f}) {
-                       $self->$f($m);
+                       $self->$f($m, \%deletions);
                } else {
                        fatal("Invalid change type: $f");
                }
@@ -5994,7 +6010,6 @@ package Git::SVN::Log;
 use strict;
 use warnings;
 use POSIX qw/strftime/;
-use Time::Local;
 use constant commit_log_separator => ('-' x 72) . "\n";
 use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline
             %rusers $show_commit $incremental/;
@@ -6104,11 +6119,8 @@ sub run_pager {
 }
 
 sub format_svn_date {
-       # some systmes don't handle or mishandle %z, so be creative.
        my $t = shift || time;
-       my $gm = timelocal(gmtime($t));
-       my $sign = qw( + + - )[ $t <=> $gm ];
-       my $gmoff = sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
+       my $gmoff = Git::SVN::get_tz($t);
        return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t));
 }
 
index 3fc7380a5ea510b705eb49542c3e698b0dddb02c..b63a5c67aff0fd587da31b307c1154ec033950f1 100755 (executable)
@@ -5633,7 +5633,7 @@ sub git_tags_body {
 
 sub git_heads_body {
        # uses global variable $project
-       my ($headlist, $head, $from, $to, $extra) = @_;
+       my ($headlist, $head_at, $from, $to, $extra) = @_;
        $from = 0 unless defined $from;
        $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
@@ -5642,7 +5642,7 @@ sub git_heads_body {
        for (my $i = $from; $i <= $to; $i++) {
                my $entry = $headlist->[$i];
                my %ref = %$entry;
-               my $curr = $ref{'id'} eq $head;
+               my $curr = defined $head_at && $ref{'id'} eq $head_at;
                if ($alternate) {
                        print "<tr class=\"dark\">\n";
                } else {
@@ -5915,9 +5915,10 @@ sub git_search_files {
        my $alternate = 1;
        my $matches = 0;
        my $lastfile = '';
+       my $file_href;
        while (my $line = <$fd>) {
                chomp $line;
-               my ($file, $file_href, $lno, $ltext, $binary);
+               my ($file, $lno, $ltext, $binary);
                last if ($matches++ > 1000);
                if ($line =~ /^Binary file (.+) matches$/) {
                        $file = $1;
diff --git a/help.c b/help.c
index cbbe966f685b276cac702bb0fd9a44bfbf5f0e79..14eefc91ced3890975d0833ffc83971c7986858b 100644 (file)
--- a/help.c
+++ b/help.c
@@ -5,28 +5,6 @@
 #include "help.h"
 #include "common-cmds.h"
 
-/* most GUI terminals set COLUMNS (although some don't export it) */
-static int term_columns(void)
-{
-       char *col_string = getenv("COLUMNS");
-       int n_cols;
-
-       if (col_string && (n_cols = atoi(col_string)) > 0)
-               return n_cols;
-
-#ifdef TIOCGWINSZ
-       {
-               struct winsize ws;
-               if (!ioctl(1, TIOCGWINSZ, &ws)) {
-                       if (ws.ws_col)
-                               return ws.ws_col;
-               }
-       }
-#endif
-
-       return 80;
-}
-
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
        struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);
diff --git a/pager.c b/pager.c
index 975955ba82a0dbb128d6733090cd74c2b509ea81..05584dead6728ceff818630fbccaa91bb6c6b686 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -76,6 +76,12 @@ void setup_pager(void)
        if (!pager)
                return;
 
+       /*
+        * force computing the width of the terminal before we redirect
+        * the standard output to the pager.
+        */
+       (void) term_columns();
+
        setenv("GIT_PAGER_IN_USE", "true", 1);
 
        /* spawn the pager */
@@ -110,3 +116,46 @@ int pager_in_use(void)
        env = getenv("GIT_PAGER_IN_USE");
        return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
 }
+
+/*
+ * Return cached value (if set) or $COLUMNS environment variable (if
+ * set and positive) or ioctl(1, TIOCGWINSZ).ws_col (if positive),
+ * and default to 80 if all else fails.
+ */
+int term_columns(void)
+{
+       static int term_columns_at_startup;
+
+       char *col_string;
+       int n_cols;
+
+       if (term_columns_at_startup)
+               return term_columns_at_startup;
+
+       term_columns_at_startup = 80;
+
+       col_string = getenv("COLUMNS");
+       if (col_string && (n_cols = atoi(col_string)) > 0)
+               term_columns_at_startup = n_cols;
+#ifdef TIOCGWINSZ
+       else {
+               struct winsize ws;
+               if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_col)
+                       term_columns_at_startup = ws.ws_col;
+       }
+#endif
+
+       return term_columns_at_startup;
+}
+
+/*
+ * How many columns do we need to show this number in decimal?
+ */
+int decimal_width(int number)
+{
+       int i, width;
+
+       for (width = 1, i = 10; i <= number; width++)
+               i *= 10;
+       return width;
+}
index 523ce9c45b75d85a129015a56004473a5fccf926..5b8ebd805378dc79e449ec1eecd1e97e333a7902 100755 (executable)
@@ -191,4 +191,30 @@ test_expect_success 'cleaned up' '
        test_cmp empty actual
 '
 
+test_expect_success 'rev-list --verify-objects' '
+       git rev-list --verify-objects --all >/dev/null 2>out &&
+       test_cmp empty out
+'
+
+test_expect_success 'rev-list --verify-objects with bad sha1' '
+       sha=$(echo blob | git hash-object -w --stdin) &&
+       old=$(echo $sha | sed "s+^..+&/+") &&
+       new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+       sha="$(dirname $new)$(basename $new)" &&
+       mv .git/objects/$old .git/objects/$new &&
+       test_when_finished "remove_object $sha" &&
+       git update-index --add --cacheinfo 100644 $sha foo &&
+       test_when_finished "git read-tree -u --reset HEAD" &&
+       tree=$(git write-tree) &&
+       test_when_finished "remove_object $tree" &&
+       cmt=$(echo bogus | git commit-tree $tree) &&
+       test_when_finished "remove_object $cmt" &&
+       git update-ref refs/heads/bogus $cmt &&
+       test_when_finished "git update-ref -d refs/heads/bogus" &&
+
+       test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out &&
+       cat out &&
+       grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
+'
+
 test_done
index 8341fc4d154f6d50bf9055b206810dea4e1b807b..35ec294d9a56d5fc6b22ee2a39166325352bd639 100755 (executable)
@@ -58,6 +58,11 @@ test_expect_success 'fetch with transfer.fsckobjects' '
        )
 '
 
+cat >exp <<EOF
+To dst
+!      refs/heads/master:refs/heads/test       [remote rejected] (missing necessary objects)
+EOF
+
 test_expect_success 'push without strict' '
        rm -rf dst &&
        git init dst &&
@@ -66,7 +71,8 @@ test_expect_success 'push without strict' '
                git config fetch.fsckobjects false &&
                git config transfer.fsckobjects false
        ) &&
-       git push dst master:refs/heads/test
+       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+       test_cmp exp act
 '
 
 test_expect_success 'push with !receive.fsckobjects' '
@@ -77,9 +83,15 @@ test_expect_success 'push with !receive.fsckobjects' '
                git config receive.fsckobjects false &&
                git config transfer.fsckobjects true
        ) &&
-       git push dst master:refs/heads/test
+       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+       test_cmp exp act
 '
 
+cat >exp <<EOF
+To dst
+!      refs/heads/master:refs/heads/test       [remote rejected] (n/a (unpacker error))
+EOF
+
 test_expect_success 'push with receive.fsckobjects' '
        rm -rf dst &&
        git init dst &&
@@ -88,7 +100,8 @@ test_expect_success 'push with receive.fsckobjects' '
                git config receive.fsckobjects true &&
                git config transfer.fsckobjects false
        ) &&
-       test_must_fail git push dst master:refs/heads/test
+       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+       test_cmp exp act
 '
 
 test_expect_success 'push with transfer.fsckobjects' '
@@ -98,7 +111,8 @@ test_expect_success 'push with transfer.fsckobjects' '
                cd dst &&
                git config transfer.fsckobjects true
        ) &&
-       test_must_fail git push dst master:refs/heads/test
+       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+       test_cmp exp act
 '
 
 test_done
index 9ee52cfc458b2914ee82da6ac1117a2b1ae45a64..3683df13a6ae2208212bd3606187f9876e0562b5 100755 (executable)
@@ -101,10 +101,11 @@ test_expect_success TTY 'push -q suppresses progress' '
        ! grep "Writing objects" err
 '
 
-test_expect_failure TTY 'push --no-progress suppresses progress' '
+test_expect_success TTY 'push --no-progress suppresses progress' '
        ensure_fresh_upstream &&
 
        test_terminal git push -u --no-progress upstream master >out 2>err &&
+       ! grep "Unpacking objects" err &&
        ! grep "Writing objects" err
 '
 
index d66ed2450854c3091105a43bb2aa0f831140ed1f..cc6f081711002b42bcf6b2cb26287dcc56852a06 100755 (executable)
@@ -106,7 +106,7 @@ cat >exp <<EOF
 remote: error: hook declined to update refs/heads/dev2
 To http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git
  ! [remote rejected] dev2 -> dev2 (hook declined)
-error: failed to push some refs to 'http://127.0.0.1:5541/smart/test_repo.git'
+error: failed to push some refs to 'http://127.0.0.1:$LIB_HTTPD_PORT/smart/test_repo.git'
 EOF
 
 test_expect_success 'rejected update prints status' '
index 4ef79aabc47a4ef2e9def65253edf6bcd8ce91a3..f8c247a7500d723e46796e7b9b76b9812e35db9b 100755 (executable)
@@ -1282,4 +1282,43 @@ test_expect_success 'mixing incompatibles modes and options is forbidden' '
        test_must_fail git tag -v -s
 '
 
+# check points-at
+
+test_expect_success '--points-at cannot be used in non-list mode' '
+       test_must_fail git tag --points-at=v4.0 foo
+'
+
+test_expect_success '--points-at finds lightweight tags' '
+       echo v4.0 >expect &&
+       git tag --points-at v4.0 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of commits' '
+       git tag -m "v4.0, annotated" annotated-v4.0 v4.0 &&
+       echo annotated-v4.0 >expect &&
+       git tag -l --points-at v4.0 "annotated*" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--points-at finds annotated tags of tags' '
+       git tag -m "describing the v4.0 tag object" \
+               annotated-again-v4.0 annotated-v4.0 &&
+       cat >expect <<-\EOF &&
+       annotated-again-v4.0
+       annotated-v4.0
+       EOF
+       git tag --points-at=annotated-v4.0 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'multiple --points-at are OR-ed together' '
+       cat >expect <<-\EOF &&
+       v2.0
+       v3.0
+       EOF
+       git tag --points-at=v2.0 --points-at=v3.0 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index b041516a1d6316dd36657b582c15100c0a7359d0..749b75e8d4fba546b22a0280b72891c38b5ea00a 100755 (executable)
@@ -65,7 +65,8 @@ test_expect_success "$name" "
        git update-index --add dir/file/file &&
        git commit -m '$name' &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch" || true
+               ${remotes_git_svn}..mybranch
+"
 
 
 name='detect node change from directory to file #1'
@@ -79,7 +80,8 @@ test_expect_success "$name" '
        git update-index --add -- bar &&
        git commit -m "$name" &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch2' || true
+               ${remotes_git_svn}..mybranch2
+'
 
 
 name='detect node change from file to directory #2'
@@ -92,9 +94,12 @@ test_expect_success "$name" '
        echo yyy > bar/zzz/yyy &&
        git update-index --add bar/zzz/yyy &&
        git commit -m "$name" &&
-       test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch3' || true
-
+       git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch3 &&
+       svn_cmd up "$SVN_TREE" &&
+       test -d "$SVN_TREE"/bar/zzz &&
+       test -e "$SVN_TREE"/bar/zzz/yyy
+'
 
 name='detect node change from directory to file #2'
 test_expect_success "$name" '
@@ -107,7 +112,8 @@ test_expect_success "$name" '
        git update-index --add -- dir &&
        git commit -m "$name" &&
        test_must_fail git svn set-tree --find-copies-harder --rmdir \
-               ${remotes_git_svn}..mybranch4' || true
+               ${remotes_git_svn}..mybranch4
+'
 
 
 name='remove executable bit from a file'
@@ -134,10 +140,10 @@ test_expect_success "$name" '
        test -x "$SVN_TREE"/exec.sh'
 
 
-name='executable file becomes a symlink to bar/zzz (file)'
+name='executable file becomes a symlink to file'
 test_expect_success "$name" '
        rm exec.sh &&
-       ln -s bar/zzz exec.sh &&
+       ln -s file exec.sh &&
        git update-index exec.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
@@ -148,19 +154,19 @@ test_expect_success "$name" '
 name='new symlink is added to a file that was also just made executable'
 
 test_expect_success "$name" '
-       chmod +x bar/zzz &&
-       ln -s bar/zzz exec-2.sh &&
-       git update-index --add bar/zzz exec-2.sh &&
+       chmod +x file &&
+       ln -s file exec-2.sh &&
+       git update-index --add file exec-2.sh &&
        git commit -m "$name" &&
        git svn set-tree --find-copies-harder --rmdir \
                ${remotes_git_svn}..mybranch5 &&
        svn_cmd up "$SVN_TREE" &&
-       test -x "$SVN_TREE"/bar/zzz &&
+       test -x "$SVN_TREE"/file &&
        test -h "$SVN_TREE"/exec-2.sh'
 
 name='modify a symlink to become a file'
 test_expect_success "$name" '
-       echo git help > help || true &&
+       echo git help >help &&
        rm exec-2.sh &&
        cp help exec-2.sh &&
        git update-index exec-2.sh &&
@@ -195,14 +201,15 @@ name='check imported tree checksums expected tree checksums'
 rm -f expected
 if test_have_prereq UTF8
 then
-       echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
+       echo tree dc68b14b733e4ec85b04ab6f712340edc5dc936e > expected
 fi
 cat >> expected <<\EOF
-tree 83654bb36f019ae4fe77a0171f81075972087624
-tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
-tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
-tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
-tree 56a30b966619b863674f5978696f4a3594f2fca9
+tree c3322890dcf74901f32d216f05c5044f670ce632
+tree d3ccd5035feafd17b030c5732e7808cc49122853
+tree d03e1630363d4881e68929d532746b20b0986b83
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
+tree 312b76e4f64ce14893aeac8591eb3960b065e247
+tree 149d63cd5878155c846e8c55d7d8487de283f89e
 tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
 tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
 EOF
index 0f771c673d58009e2bcde45254d4b4e9fa68efb9..90bb6050c13ece02199b6978e43e3c67de563c84 100755 (executable)
@@ -637,6 +637,45 @@ test_expect_success \
        'config override: tree view, features enabled in repo config (2)' \
        'gitweb_run "p=.git;a=tree"'
 
+# ----------------------------------------------------------------------
+# searching
+
+cat >>gitweb_config.perl <<\EOF
+
+# enable search
+$feature{'search'}{'default'} = [1];
+$feature{'grep'}{'default'} = [1];
+$feature{'pickaxe'}{'default'} = [1];
+EOF
+
+test_expect_success \
+       'search: preparation' \
+       'echo "1st MATCH" >>file &&
+        echo "2nd MATCH" >>file &&
+        echo "MATCH" >>bar &&
+        git add file bar &&
+        git commit -m "Added MATCH word"'
+
+test_expect_success \
+       'search: commit author' \
+       'gitweb_run "p=.git;a=search;h=HEAD;st=author;s=A+U+Thor"'
+
+test_expect_success \
+       'search: commit message' \
+       'gitweb_run "p=.git;a=search;h=HEAD;st=commitr;s=MATCH"'
+
+test_expect_success \
+       'search: grep' \
+       'gitweb_run "p=.git;a=search;h=HEAD;st=grep;s=MATCH"'
+
+test_expect_success \
+       'search: pickaxe' \
+       'gitweb_run "p=.git;a=search;h=HEAD;st=pickaxe;s=MATCH"'
+
+test_expect_success \
+       'search: projects' \
+       'gitweb_run "a=project_list;s=.git"'
+
 # ----------------------------------------------------------------------
 # non-ASCII in README.html
 
@@ -739,4 +778,13 @@ test_expect_success \
        'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl &&
         gitweb_run'
 
+# ----------------------------------------------------------------------
+# unborn branches
+
+test_expect_success \
+       'unborn HEAD: "summary" page (with "heads" subview)' \
+       'git checkout orphan_branch || git checkout --orphan orphan_branch &&
+        test_when_finished "git checkout master" &&
+        gitweb_run "p=.git;a=summary"'
+
 test_done
index cac0c065ff9f82011b204f932932283b01a5d034..401b8dd35ce9acd88f38440fc52b908898119a81 100644 (file)
@@ -993,11 +993,15 @@ void transport_set_verbosity(struct transport *transport, int verbosity,
         * Rules used to determine whether to report progress (processing aborts
         * when a rule is satisfied):
         *
-        *   1. Report progress, if force_progress is 1 (ie. --progress).
-        *   2. Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
-        *   3. Report progress if isatty(2) is 1.
+        *   . Report progress, if force_progress is 1 (ie. --progress).
+        *   . Don't report progress, if force_progress is 0 (ie. --no-progress).
+        *   . Don't report progress, if verbosity < 0 (ie. -q/--quiet ).
+        *   . Report progress if isatty(2) is 1.
         **/
-       transport->progress = force_progress || (verbosity >= 0 && isatty(2));
+       if (force_progress >= 0)
+               transport->progress = !!force_progress;
+       else
+               transport->progress = verbosity >= 0 && isatty(2);
 }
 
 int transport_push(struct transport *transport,