Code

Merge branch 'js/filter' into ei/worktree+filter
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Jun 2007 23:08:34 +0000 (16:08 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Jun 2007 23:08:34 +0000 (16:08 -0700)
* js/filter:
  filter-branch: also don't fail in map() if a commit cannot be mapped
  filter-branch: Use rev-list arguments to specify revision ranges.
  filter-branch: fix behaviour of '-k'
  filter-branch: use $(($i+1)) instead of $((i+1))
  chmod +x git-filter-branch.sh
  filter-branch: prevent filters from reading from stdin
  t7003: make test repeatable
  Add git-filter-branch

44 files changed:
.gitignore
Documentation/config.txt
Documentation/git-format-patch.txt
Documentation/git-fsck.txt
Documentation/git-rev-parse.txt
Documentation/git-tag.txt
Documentation/git-unpack-objects.txt
Documentation/git.txt
Makefile
builtin-branch.c
builtin-fsck.c
builtin-log.c
builtin-ls-files.c
builtin-pack-objects.c
builtin-rev-parse.c
cache.h
connect.c
date.c
diff.c
git-cvsimport.perl
git-mergetool.sh
git-sh-setup.sh
git-svn.perl
git-tag.sh
git.c
gitweb/gitweb.perl
grep.c
ident.c
index-pack.c
merge-recursive.c
object.c
pack-write.c
pack.h
setup.c
t/t1500-rev-parse.sh [new file with mode: 0755]
t/t1501-worktree.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t5000-tar-tree.sh
t/t6024-recursive-merge.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
tree.c
xdiff-interface.c
xdiff-interface.h

index 15aed7063134c041011fe3582627ad19c3f381da..27e5aeb8a078a1bac61ce207e56338881ae57a07 100644 (file)
@@ -40,6 +40,7 @@ git-fast-import
 git-fetch
 git-fetch--tool
 git-fetch-pack
+git-filter-branch
 git-findtags
 git-fmt-merge-msg
 git-for-each-ref
@@ -151,6 +152,7 @@ test-delta
 test-dump-cache-tree
 test-genrandom
 test-match-trees
+test-sha1
 common-cmds.h
 *.tar.gz
 *.dsc
index 5868d587a905e0aaa55194c7716f9e258a030474..4d0bd37af9c74403b25e1d6bc1ae944791c3e179 100644 (file)
@@ -172,6 +172,13 @@ repository that ends in "/.git" is assumed to be not bare (bare =
 false), while all other repositories are assumed to be bare (bare
 = true).
 
+core.worktree::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can be overriden by the GIT_WORK_TREE environment
+       variable and the '--work-tree' command line option.
+
 core.logAllRefUpdates::
        Updates to a ref <ref> is logged to the file
        "$GIT_DIR/logs/<ref>", by appending the new and old
index a33d157b970740aa7d056ebb459350de89513a8b..363edb0fcfd140188507baa93cd2be9b6923d00a 100644 (file)
@@ -11,7 +11,8 @@ SYNOPSIS
 [verse]
 'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
                    [--attach[=<boundary>] | --inline[=<boundary>]]
-                   [-s | --signoff] [<common diff options>] [--start-number <n>]
+                   [-s | --signoff] [<common diff options>]
+                   [--start-number <n>] [--numbered-files]
                    [--in-reply-to=Message-Id] [--suffix=.<sfx>]
                    [--ignore-if-in-upstream]
                    [--subject-prefix=Subject-Prefix]
@@ -30,9 +31,11 @@ gitlink:git-rev-parse[1].
 The output of this command is convenient for e-mail submission or
 for use with gitlink:git-am[1].
 
-Each output file is numbered sequentially from 1, and uses the
+By default, each output file is numbered sequentially from 1, and uses the
 first line of the commit message (massaged for pathname safety) as
-the filename. The names of the output files are printed to standard
+the filename. With the --numbered-files option, the output file names
+will only be numbers, without the first line of the commit appended.
+The names of the output files are printed to standard
 output, unless the --stdout option is specified.
 
 If -o is specified, output files are created in <dir>.  Otherwise
@@ -60,6 +63,11 @@ include::diff-options.txt[]
 --start-number <n>::
        Start numbering the patches at <n> instead of 1.
 
+--numbered-files::
+       Output file names will be a simple number sequence
+       without the default first line of the commit appended.
+       Mutually exclusive with the --stdout option.
+
 -k|--keep-subject::
        Do not strip/add '[PATCH]' from the first line of the
        commit log message.
index 8c68cf037259b3abc7ea16952d232b2fb2f07a25..ed6413a3c75625ea63a5ea140ae15b120997c9b4 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
-                [--full] [--strict] [<object>*]
+                [--full] [--strict] [--verbose] [<object>*]
 
 DESCRIPTION
 -----------
@@ -61,6 +61,9 @@ index file and all SHA1 references in .git/refs/* as heads.
        objects that triggers this check, but it is recommended
        to check new projects with this flag.
 
+--verbose::
+       Be chatty.
+
 It tests SHA1 and general object sanity, and it does full tracking of
 the resulting reachability and everything else. It prints out any
 corruption it finds (missing or bad objects), and if you use the
index 7757abe62190a066b6ad8b57214b70ff3fa0f12a..6e4d15829d4607b9f5b1bafdbd616643266a2538 100644 (file)
@@ -89,6 +89,17 @@ OPTIONS
 --git-dir::
        Show `$GIT_DIR` if defined else show the path to the .git directory.
 
+--is-inside-git-dir::
+       When the current working directory is below the repository
+       directory print "true", otherwise "false".
+
+--is-inside-work-tree::
+       When the current working directory is inside the work tree of the
+       repository print "true", otherwise "false".
+
+--is-bare-repository::
+       When the repository is bare print "true", otherwise "false".
+
 --short, --short=number::
        Instead of outputting the full SHA1 values of object names try to
        abbreviate them to a shorter unique name. When no length is specified
index 4e3e02756c8863dbc6fe1d745e1a8c9a97c18919..aee2c1bdc785259c3f587323b557f5c575f3c797 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]  <name> [<head>]
 'git-tag' -d <name>...
-'git-tag' -l [<pattern>]
+'git-tag' [-n [<num>]] -l [<pattern>]
 'git-tag' -v <name>
 
 DESCRIPTION
@@ -38,8 +38,8 @@ GnuPG key for signing.
 
 `-v <tag>` verifies the gpg signature of the tag.
 
-`-l <pattern>` lists tags that match the given pattern (or all
-if no pattern is given).
+`-l <pattern>` lists tags with names that match the given pattern
+(or all if no pattern is given).
 
 OPTIONS
 -------
@@ -61,8 +61,13 @@ OPTIONS
 -v::
        Verify the gpg signature of given the tag
 
+-n <num>::
+       <num> specifies how many lines from the annotation, if any,
+       are printed when using -l.
+       The default is not to print any annotation lines.
+
 -l <pattern>::
-       List tags that match the given pattern (or all if no pattern is given).
+       List tags with names that match the given pattern (or all if no pattern is given).
 
 -m <msg>::
        Use the given tag message (instead of prompting)
index ff6184b0f7498a44995fe4c55dab49c5517ec458..b1b3ec9772b7e689a4a3d42e78be68df34de40ac 100644 (file)
@@ -27,8 +27,8 @@ new packs and replace existing ones.
 OPTIONS
 -------
 -n::
-        Only list the objects that would be unpacked, don't actually unpack
-        them.
+        Dry run.  Check the pack file without actually unpacking
+       the objects.
 
 -q::
        The command usually shows percentage progress.  This
index 98860af0455ebb5290e7225e6ac677dc7ab3268c..4b567d8028ba78c3182e5416394786b5cded6b45 100644 (file)
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
-    [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
+    [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+    [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -101,6 +102,14 @@ OPTIONS
        Set the path to the repository. This can also be controlled by
        setting the GIT_DIR environment variable.
 
+--work-tree=<path>::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can also be controlled by setting the GIT_WORK_TREE
+       environment variable and the core.worktree configuration
+       variable.
+
 --bare::
        Same as --git-dir=`pwd`.
 
@@ -345,6 +354,13 @@ git so take care if using Cogito etc.
        specifies a path to use instead of the default `.git`
        for the base of the repository.
 
+'GIT_WORK_TREE'::
+       Set the path to the working tree.  The value will not be
+       used in combination with repositories found automatically in
+       a .git directory (i.e. $GIT_DIR is not set).
+       This can also be controlled by the '--work-tree' command line
+       option and the core.worktree configuration variable.
+
 git Commits
 ~~~~~~~~~~~
 'GIT_AUTHOR_NAME'::
index 69f3b667144ee77f12219ac0768cb3f1e4409944..2d8c3089a63ddf6a39b4353d11c82e9765095c1e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -239,7 +239,6 @@ PROGRAMS = \
        git-convert-objects$X git-fetch-pack$X \
        git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-fast-import$X \
-       git-merge-base$X \
        git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
        git-peek-remote$X git-receive-pack$X \
@@ -1052,8 +1051,9 @@ dist-doc:
 
 clean:
        rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
-               test-chmtime$X test-genrandom$X $(LIB_FILE) $(XDIFF_LIB)
+               $(LIB_FILE) $(XDIFF_LIB)
        rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
+       rm -f $(TEST_PROGRAMS)
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
        rm -rf autom4te.cache
        rm -f configure config.log config.mak.autogen config.mak.append config.status config.cache
index a5b6bbef6e903991cf70fd5978f5f89c48c60fb5..67f46c1ae2fd28fe6b4baffdf3447df7904cc021 100644 (file)
@@ -317,8 +317,6 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
 static char *config_repo;
 static char *config_remote;
 static const char *start_ref;
-static int start_len;
-static int base_len;
 
 static int get_remote_branch_name(const char *value)
 {
@@ -334,26 +332,41 @@ static int get_remote_branch_name(const char *value)
 
        end = value + strlen(value);
 
-       /* Try an exact match first.  */
+       /*
+        * Try an exact match first.  I.e. handle the case where the
+        * value is "$anything:refs/foo/bar/baz" and start_ref is exactly
+        * "refs/foo/bar/baz". Then the name at the remote is $anything.
+        */
        if (!strcmp(colon + 1, start_ref)) {
-               /* Truncate the value before the colon.  */
+               /* Truncate the value before the colon. */
                nfasprintf(&config_repo, "%.*s", colon - value, value);
                return 1;
        }
 
-       /* Try with a wildcard match now.  */
-       if (end - value > 2 && end[-2] == '/' && end[-1] == '*' &&
-           colon - value > 2 && colon[-2] == '/' && colon[-1] == '*' &&
-           (end - 2) - (colon + 1) == base_len &&
-           !strncmp(colon + 1, start_ref, base_len)) {
-               /* Replace the star with the remote branch name.  */
-               nfasprintf(&config_repo, "%.*s%s",
-                          (colon - 2) - value, value,
-                          start_ref + base_len);
-               return 1;
-       }
+       /*
+        * Is this a wildcard match?
+        */
+       if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' ||
+           (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*')
+               return 0;
 
-       return 0;
+       /*
+        * Value is "refs/foo/bar/<asterisk>:refs/baz/boa/<asterisk>"
+        * and start_ref begins with "refs/baz/boa/"; the name at the
+        * remote is refs/foo/bar/ with the remaining part of the
+        * start_ref.  The length of the prefix on the RHS is (end -
+        * colon - 2), including the slash immediately before the
+        * asterisk.
+        */
+       if ((strlen(start_ref) < end - colon - 2) ||
+           memcmp(start_ref, colon + 1, end - colon - 2))
+               return 0; /* does not match prefix */
+
+       /* Replace the asterisk with the remote branch name.  */
+       nfasprintf(&config_repo, "%.*s%s",
+                  (colon - 1) - value, value,
+                  start_ref + (end - colon - 2));
+       return 1;
 }
 
 static int get_remote_config(const char *key, const char *value)
@@ -363,10 +376,12 @@ static int get_remote_config(const char *key, const char *value)
                return 0;
 
        var = strrchr(key, '.');
-       if (var == key + 6)
+       if (var == key + 6 || strcmp(var, ".fetch"))
                return 0;
-
-       if (!strcmp(var, ".fetch") && get_remote_branch_name(value))
+       /*
+        * Ok, we are looking at key == "remote.$foo.fetch";
+        */
+       if (get_remote_branch_name(value))
                nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7);
 
        return 0;
@@ -392,14 +407,14 @@ static void set_branch_merge(const char *name, const char *config_remote,
 
 static void set_branch_defaults(const char *name, const char *real_ref)
 {
-       const char *slash = strrchr(real_ref, '/');
-
-       if (!slash)
-               return;
-
+       /*
+        * name is the name of new branch under refs/heads;
+        * real_ref is typically refs/remotes/$foo/$bar, where
+        * $foo is the remote name (there typically are no slashes)
+        * and $bar is the branch name we map from the remote
+        * (it could have slashes).
+        */
        start_ref = real_ref;
-       start_len = strlen(real_ref);
-       base_len = slash - real_ref;
        git_config(get_remote_config);
        if (!config_repo && !config_remote &&
            !prefixcmp(real_ref, "refs/heads/")) {
index 9959818ceddf52ec24d9b24b0cade558f4d43070..bacae5dfa6e09602db59df256e5496f9f14e0d89 100644 (file)
@@ -20,6 +20,7 @@ static int check_strict;
 static int keep_cache_objects;
 static unsigned char head_sha1[20];
 static int errors_found;
+static int verbose;
 #define ERROR_OBJECT 01
 #define ERROR_REACHABLE 02
 
@@ -149,6 +150,9 @@ static void check_unreachable_object(struct object *obj)
 
 static void check_object(struct object *obj)
 {
+       if (verbose)
+               fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
+
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
        else
@@ -161,6 +165,9 @@ static void check_connectivity(void)
 
        /* Look up all the requirements, warn about missing objects.. */
        max = get_max_object_index();
+       if (verbose)
+               fprintf(stderr, "Checking connectivity (%d objects)\n", max);
+
        for (i = 0; i < max; i++) {
                struct object *obj = get_indexed_object(i);
 
@@ -229,6 +236,10 @@ static int fsck_tree(struct tree *item)
        const char *o_name;
        const unsigned char *o_sha1;
 
+       if (verbose)
+               fprintf(stderr, "Checking tree %s\n",
+                               sha1_to_hex(item->object.sha1));
+
        init_tree_desc(&desc, item->buffer, item->size);
 
        o_mode = 0;
@@ -317,6 +328,10 @@ static int fsck_commit(struct commit *commit)
        char *buffer = commit->buffer;
        unsigned char tree_sha1[20], sha1[20];
 
+       if (verbose)
+               fprintf(stderr, "Checking commit %s\n",
+                       sha1_to_hex(commit->object.sha1));
+
        if (memcmp(buffer, "tree ", 5))
                return objerror(&commit->object, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
@@ -345,6 +360,10 @@ static int fsck_tag(struct tag *tag)
 {
        struct object *tagged = tag->tagged;
 
+       if (verbose)
+               fprintf(stderr, "Checking tag %s\n",
+                       sha1_to_hex(tag->object.sha1));
+
        if (!tagged) {
                return objerror(&tag->object, "could not load tagged object");
        }
@@ -446,6 +465,9 @@ static void fsck_dir(int i, char *path)
        if (!dir)
                return;
 
+       if (verbose)
+               fprintf(stderr, "Checking directory %s\n", path);
+
        while ((de = readdir(dir)) != NULL) {
                char name[100];
                unsigned char sha1[20];
@@ -480,6 +502,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 {
        struct object *obj;
 
+       if (verbose)
+               fprintf(stderr, "Checking reflog %s->%s\n",
+                       sha1_to_hex(osha1), sha1_to_hex(nsha1));
+
        if (!is_null_sha1(osha1)) {
                obj = lookup_object(osha1);
                if (obj) {
@@ -549,6 +575,10 @@ static void get_default_heads(void)
 static void fsck_object_dir(const char *path)
 {
        int i;
+
+       if (verbose)
+               fprintf(stderr, "Checking object directory\n");
+
        for (i = 0; i < 256; i++) {
                static char dir[4096];
                sprintf(dir, "%s/%02x", path, i);
@@ -564,6 +594,9 @@ static int fsck_head_link(void)
        int null_is_error = 0;
        const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
 
+       if (verbose)
+               fprintf(stderr, "Checking HEAD link\n");
+
        if (!head_points_at)
                return error("Invalid HEAD");
        if (!strcmp(head_points_at, "HEAD"))
@@ -586,6 +619,9 @@ static int fsck_cache_tree(struct cache_tree *it)
        int i;
        int err = 0;
 
+       if (verbose)
+               fprintf(stderr, "Checking cache tree\n");
+
        if (0 <= it->entry_count) {
                struct object *obj = parse_object(it->sha1);
                if (!obj) {
@@ -605,7 +641,7 @@ static int fsck_cache_tree(struct cache_tree *it)
 
 static const char fsck_usage[] =
 "git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] "
-"[--strict] <head-sha1>*]";
+"[--strict] [--verbose] <head-sha1>*]";
 
 int cmd_fsck(int argc, char **argv, const char *prefix)
 {
@@ -645,6 +681,10 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
                        check_strict = 1;
                        continue;
                }
+               if (!strcmp(arg, "--verbose")) {
+                       verbose = 1;
+                       continue;
+               }
                if (*arg == '-')
                        usage(fsck_usage);
        }
index 37447123f924149f012a298eaf31dacb0c87b724..212cdfc769b58403a99155e321ea36f018061ea4 100644 (file)
@@ -298,7 +298,8 @@ static int git_format_config(const char *var, const char *value)
 static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
 
-static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
+static int reopen_stdout(struct commit *commit, int nr, int keep_subject,
+                        int numbered_files)
 {
        char filename[PATH_MAX];
        char *sol;
@@ -315,53 +316,61 @@ static int reopen_stdout(struct commit *commit, int nr, int keep_subject)
                        filename[len++] = '/';
        }
 
-       sprintf(filename + len, "%04d", nr);
-       len = strlen(filename);
-
-       sol = strstr(commit->buffer, "\n\n");
-       if (sol) {
-               int j, space = 1;
-
-               sol += 2;
-               /* strip [PATCH] or [PATCH blabla] */
-               if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
-                       char *eos = strchr(sol + 6, ']');
-                       if (eos) {
-                               while (isspace(*eos))
-                                       eos++;
-                               sol = eos;
-                       }
-               }
+       if (numbered_files) {
+               sprintf(filename + len, "%d", nr);
+               len = strlen(filename);
 
-               for (j = 0;
-                    j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
-                            len < sizeof(filename) - suffix_len &&
-                            sol[j] && sol[j] != '\n';
-                    j++) {
-                       if (istitlechar(sol[j])) {
-                               if (space) {
-                                       filename[len++] = '-';
-                                       space = 0;
+       } else {
+               sprintf(filename + len, "%04d", nr);
+               len = strlen(filename);
+
+               sol = strstr(commit->buffer, "\n\n");
+               if (sol) {
+                       int j, space = 1;
+
+                       sol += 2;
+                       /* strip [PATCH] or [PATCH blabla] */
+                       if (!keep_subject && !prefixcmp(sol, "[PATCH")) {
+                               char *eos = strchr(sol + 6, ']');
+                               if (eos) {
+                                       while (isspace(*eos))
+                                               eos++;
+                                       sol = eos;
                                }
-                               filename[len++] = sol[j];
-                               if (sol[j] == '.')
-                                       while (sol[j + 1] == '.')
-                                               j++;
-                       } else
-                               space = 1;
+                       }
+
+                       for (j = 0;
+                            j < FORMAT_PATCH_NAME_MAX - suffix_len - 5 &&
+                                    len < sizeof(filename) - suffix_len &&
+                                    sol[j] && sol[j] != '\n';
+                            j++) {
+                               if (istitlechar(sol[j])) {
+                                       if (space) {
+                                               filename[len++] = '-';
+                                               space = 0;
+                                       }
+                                       filename[len++] = sol[j];
+                                       if (sol[j] == '.')
+                                               while (sol[j + 1] == '.')
+                                                       j++;
+                               } else
+                                       space = 1;
+                       }
+                       while (filename[len - 1] == '.'
+                              || filename[len - 1] == '-')
+                               len--;
+                       filename[len] = 0;
                }
-               while (filename[len - 1] == '.' || filename[len - 1] == '-')
-                       len--;
-               filename[len] = 0;
+               if (len + suffix_len >= sizeof(filename))
+                       return error("Patch pathname too long");
+               strcpy(filename + len, fmt_patch_suffix);
        }
-       if (len + suffix_len >= sizeof(filename))
-               return error("Patch pathname too long");
-       strcpy(filename + len, fmt_patch_suffix);
+
        fprintf(realstdout, "%s\n", filename);
        if (freopen(filename, "w", stdout) == NULL)
                return error("Cannot open patch file %s",filename);
-       return 0;
 
+       return 0;
 }
 
 static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
@@ -431,6 +440,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int numbered = 0;
        int start_number = -1;
        int keep_subject = 0;
+       int numbered_files = 0;         /* _just_ numbers */
        int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
        int thread = 0;
@@ -465,6 +475,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        numbered = 1;
                else if (!prefixcmp(argv[i], "--start-number="))
                        start_number = strtol(argv[i] + 15, NULL, 10);
+               else if (!strcmp(argv[i], "--numbered-files"))
+                       numbered_files = 1;
                else if (!strcmp(argv[i], "--start-number")) {
                        i++;
                        if (i == argc)
@@ -540,6 +552,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                die ("-n and -k are mutually exclusive.");
        if (keep_subject && subject_prefix)
                die ("--subject-prefix and -k are mutually exclusive.");
+       if (numbered_files && use_stdout)
+               die ("--numbered-files and --stdout are mutually exclusive.");
 
        argc = setup_revisions(argc, argv, &rev, "HEAD");
        if (argc > 1)
@@ -614,7 +628,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        rev.message_id = message_id;
                }
                if (!use_stdout)
-                       if (reopen_stdout(commit, rev.nr, keep_subject))
+                       if (reopen_stdout(commit, rev.nr, keep_subject,
+                                         numbered_files))
                                die("Failed to create output files");
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
index f7c066b24b7a6a728fd2f0bf4a92a31fb4a695dd..48a313516db12dbb74d1f0293d3a55edc0cb3f2c 100644 (file)
@@ -470,7 +470,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        }
 
        if (require_work_tree &&
-                       (is_bare_repository() || is_inside_git_dir()))
+                       (!is_inside_work_tree() || is_inside_git_dir()))
                die("This operation must be run in a work tree");
 
        pathspec = get_pathspec(prefix, argv + i);
index ccb25f6a9c298bd977c5f55a84dd9a20a1e54e23..8b9740c27703dbbe71eb607b35520c5967279c0d 100644 (file)
@@ -23,10 +23,9 @@ git-pack-objects [{ -q | --progress | --all-progress }] [--max-pack-size=N] \n\
        [--stdout | base-name] [<ref-list | <object-list]";
 
 struct object_entry {
-       unsigned char sha1[20];
-       uint32_t crc32;         /* crc of raw pack data for this object */
-       off_t offset;           /* offset into the final pack file */
+       struct pack_idx_entry idx;
        unsigned long size;     /* uncompressed size */
+
        unsigned int hash;      /* name hint hash */
        unsigned int depth;     /* delta depth */
        struct packed_git *in_pack;     /* already in pack */
@@ -66,7 +65,6 @@ static int allow_ofs_delta;
 static const char *pack_tmp_name, *idx_tmp_name;
 static char tmpname[PATH_MAX];
 static const char *base_name;
-static unsigned char pack_file_sha1[20];
 static int progress = 1;
 static int window = 10;
 static uint32_t pack_size_limit;
@@ -246,11 +244,11 @@ static void *delta_against(void *buf, unsigned long size, struct object_entry *e
 {
        unsigned long othersize, delta_size;
        enum object_type type;
-       void *otherbuf = read_sha1_file(entry->delta->sha1, &type, &othersize);
+       void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
        void *delta_buf;
 
        if (!otherbuf)
-               die("unable to read %s", sha1_to_hex(entry->delta->sha1));
+               die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
         delta_buf = diff_delta(otherbuf, othersize,
                               buf, size, &delta_size, 0);
         if (!delta_buf || delta_size != entry->delta_size)
@@ -379,11 +377,11 @@ static unsigned long write_object(struct sha1file *f,
                                /* yes if unlimited packfile */
                                !pack_size_limit ? 1 :
                                /* no if base written to previous pack */
-                               entry->delta->offset == (off_t)-1 ? 0 :
+                               entry->delta->idx.offset == (off_t)-1 ? 0 :
                                /* otherwise double-check written to this
                                 * pack,  like we do below
                                 */
-                               entry->delta->offset ? 1 : 0;
+                               entry->delta->idx.offset ? 1 : 0;
 
        if (!pack_to_stdout)
                crc32_begin(f);
@@ -411,22 +409,22 @@ static unsigned long write_object(struct sha1file *f,
                unsigned long maxsize;
                void *out;
                if (!usable_delta) {
-                       buf = read_sha1_file(entry->sha1, &obj_type, &size);
+                       buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
                        if (!buf)
-                               die("unable to read %s", sha1_to_hex(entry->sha1));
+                               die("unable to read %s", sha1_to_hex(entry->idx.sha1));
                } else if (entry->delta_data) {
                        size = entry->delta_size;
                        buf = entry->delta_data;
                        entry->delta_data = NULL;
-                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                } else {
-                       buf = read_sha1_file(entry->sha1, &type, &size);
+                       buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
-                               die("unable to read %s", sha1_to_hex(entry->sha1));
+                               die("unable to read %s", sha1_to_hex(entry->idx.sha1));
                        buf = delta_against(buf, size, entry);
                        size = entry->delta_size;
-                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                }
                /* compress the data to store and put compressed length in datalen */
@@ -456,7 +454,7 @@ static unsigned long write_object(struct sha1file *f,
                         * encoding of the relative offset for the delta
                         * base from this object's position in the pack.
                         */
-                       off_t ofs = entry->offset - entry->delta->offset;
+                       off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
@@ -480,7 +478,7 @@ static unsigned long write_object(struct sha1file *f,
                                return 0;
                        }
                        sha1write(f, header, hdrlen);
-                       sha1write(f, entry->delta->sha1, 20);
+                       sha1write(f, entry->delta->idx.sha1, 20);
                        hdrlen += 20;
                } else {
                        if (limit && hdrlen + datalen + 20 >= limit) {
@@ -501,7 +499,7 @@ static unsigned long write_object(struct sha1file *f,
                off_t offset;
 
                if (entry->delta) {
-                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                        reused_delta++;
                }
@@ -511,11 +509,11 @@ static unsigned long write_object(struct sha1file *f,
                datalen = revidx[1].offset - offset;
                if (!pack_to_stdout && p->index_version > 1 &&
                    check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
-                       die("bad packed object CRC for %s", sha1_to_hex(entry->sha1));
+                       die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
                if (obj_type == OBJ_OFS_DELTA) {
-                       off_t ofs = entry->offset - entry->delta->offset;
+                       off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
@@ -529,7 +527,7 @@ static unsigned long write_object(struct sha1file *f,
                        if (limit && hdrlen + 20 + datalen + 20 >= limit)
                                return 0;
                        sha1write(f, header, hdrlen);
-                       sha1write(f, entry->delta->sha1, 20);
+                       sha1write(f, entry->delta->idx.sha1, 20);
                        hdrlen += 20;
                } else {
                        if (limit && hdrlen + datalen + 20 >= limit)
@@ -539,7 +537,7 @@ static unsigned long write_object(struct sha1file *f,
 
                if (!pack_to_stdout && p->index_version == 1 &&
                    check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
-                       die("corrupt packed object for %s", sha1_to_hex(entry->sha1));
+                       die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
                copy_pack_data(f, p, &w_curs, offset, datalen);
                unuse_pack(&w_curs);
                reused++;
@@ -548,7 +546,7 @@ static unsigned long write_object(struct sha1file *f,
                written_delta++;
        written++;
        if (!pack_to_stdout)
-               entry->crc32 = crc32_end(f);
+               entry->idx.crc32 = crc32_end(f);
        return hdrlen + datalen;
 }
 
@@ -559,7 +557,7 @@ static off_t write_one(struct sha1file *f,
        unsigned long size;
 
        /* offset is non zero if object is written already. */
-       if (e->offset || e->preferred_base)
+       if (e->idx.offset || e->preferred_base)
                return offset;
 
        /* if we are deltified, write out base object first. */
@@ -569,10 +567,10 @@ static off_t write_one(struct sha1file *f,
                        return 0;
        }
 
-       e->offset = offset;
+       e->idx.offset = offset;
        size = write_object(f, e, offset);
        if (!size) {
-               e->offset = 0;
+               e->idx.offset = 0;
                return 0;
        }
        written_list[nr_written++] = e;
@@ -589,8 +587,7 @@ static int open_object_dir_tmp(const char *path)
     return mkstemp(tmpname);
 }
 
-/* forward declarations for write_pack_file */
-static void write_index_file(off_t last_obj_offset, unsigned char *sha1);
+/* forward declaration for write_pack_file */
 static int adjust_perm(const char *path, mode_t mode);
 
 static void write_pack_file(void)
@@ -607,6 +604,8 @@ static void write_pack_file(void)
        written_list = xmalloc(nr_objects * sizeof(struct object_entry *));
 
        do {
+               unsigned char sha1[20];
+
                if (pack_to_stdout) {
                        f = sha1fd(1, "<stdout>");
                } else {
@@ -638,23 +637,23 @@ static void write_pack_file(void)
                 * If so, rewrite it like in fast-import
                 */
                if (pack_to_stdout || nr_written == nr_remaining) {
-                       sha1close(f, pack_file_sha1, 1);
+                       sha1close(f, sha1, 1);
                } else {
-                       sha1close(f, pack_file_sha1, 0);
-                       fixup_pack_header_footer(f->fd, pack_file_sha1, pack_tmp_name, nr_written);
+                       sha1close(f, sha1, 0);
+                       fixup_pack_header_footer(f->fd, sha1, pack_tmp_name, nr_written);
                        close(f->fd);
                }
 
                if (!pack_to_stdout) {
-                       unsigned char object_list_sha1[20];
                        mode_t mode = umask(0);
 
                        umask(mode);
                        mode = 0444 & ~mode;
 
-                       write_index_file(last_obj_offset, object_list_sha1);
+                       idx_tmp_name = write_idx_file(NULL,
+                               (struct pack_idx_entry **) written_list, nr_written, sha1);
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.pack",
-                                base_name, sha1_to_hex(object_list_sha1));
+                                base_name, sha1_to_hex(sha1));
                        if (adjust_perm(pack_tmp_name, mode))
                                die("unable to make temporary pack file readable: %s",
                                    strerror(errno));
@@ -662,19 +661,19 @@ static void write_pack_file(void)
                                die("unable to rename temporary pack file: %s",
                                    strerror(errno));
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
-                                base_name, sha1_to_hex(object_list_sha1));
+                                base_name, sha1_to_hex(sha1));
                        if (adjust_perm(idx_tmp_name, mode))
                                die("unable to make temporary index file readable: %s",
                                    strerror(errno));
                        if (rename(idx_tmp_name, tmpname))
                                die("unable to rename temporary index file: %s",
                                    strerror(errno));
-                       puts(sha1_to_hex(object_list_sha1));
+                       puts(sha1_to_hex(sha1));
                }
 
                /* mark written objects as written to previous pack */
                for (j = 0; j < nr_written; j++) {
-                       written_list[j]->offset = (off_t)-1;
+                       written_list[j]->idx.offset = (off_t)-1;
                }
                nr_remaining -= nr_written;
        } while (nr_remaining && i < nr_objects);
@@ -692,129 +691,12 @@ static void write_pack_file(void)
         */
        for (j = 0; i < nr_objects; i++) {
                struct object_entry *e = objects + i;
-               j += !e->offset && !e->preferred_base;
+               j += !e->idx.offset && !e->preferred_base;
        }
        if (j)
                die("wrote %u objects as expected but %u unwritten", written, j);
 }
 
-static int sha1_sort(const void *_a, const void *_b)
-{
-       const struct object_entry *a = *(struct object_entry **)_a;
-       const struct object_entry *b = *(struct object_entry **)_b;
-       return hashcmp(a->sha1, b->sha1);
-}
-
-static uint32_t index_default_version = 1;
-static uint32_t index_off32_limit = 0x7fffffff;
-
-static void write_index_file(off_t last_obj_offset, unsigned char *sha1)
-{
-       struct sha1file *f;
-       struct object_entry **sorted_by_sha, **list, **last;
-       uint32_t array[256];
-       uint32_t i, index_version;
-       SHA_CTX ctx;
-
-       int fd = open_object_dir_tmp("tmp_idx_XXXXXX");
-       if (fd < 0)
-               die("unable to create %s: %s\n", tmpname, strerror(errno));
-       idx_tmp_name = xstrdup(tmpname);
-       f = sha1fd(fd, idx_tmp_name);
-
-       if (nr_written) {
-               sorted_by_sha = written_list;
-               qsort(sorted_by_sha, nr_written, sizeof(*sorted_by_sha), sha1_sort);
-               list = sorted_by_sha;
-               last = sorted_by_sha + nr_written;
-       } else
-               sorted_by_sha = list = last = NULL;
-
-       /* if last object's offset is >= 2^31 we should use index V2 */
-       index_version = (last_obj_offset >> 31) ? 2 : index_default_version;
-
-       /* index versions 2 and above need a header */
-       if (index_version >= 2) {
-               struct pack_idx_header hdr;
-               hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
-               hdr.idx_version = htonl(index_version);
-               sha1write(f, &hdr, sizeof(hdr));
-       }
-
-       /*
-        * Write the first-level table (the list is sorted,
-        * but we use a 256-entry lookup to be able to avoid
-        * having to do eight extra binary search iterations).
-        */
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = list;
-               while (next < last) {
-                       struct object_entry *entry = *next;
-                       if (entry->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - sorted_by_sha);
-               list = next;
-       }
-       sha1write(f, array, 256 * 4);
-
-       /* Compute the SHA1 hash of sorted object names. */
-       SHA1_Init(&ctx);
-
-       /* Write the actual SHA1 entries. */
-       list = sorted_by_sha;
-       for (i = 0; i < nr_written; i++) {
-               struct object_entry *entry = *list++;
-               if (index_version < 2) {
-                       uint32_t offset = htonl(entry->offset);
-                       sha1write(f, &offset, 4);
-               }
-               sha1write(f, entry->sha1, 20);
-               SHA1_Update(&ctx, entry->sha1, 20);
-       }
-
-       if (index_version >= 2) {
-               unsigned int nr_large_offset = 0;
-
-               /* write the crc32 table */
-               list = sorted_by_sha;
-               for (i = 0; i < nr_written; i++) {
-                       struct object_entry *entry = *list++;
-                       uint32_t crc32_val = htonl(entry->crc32);
-                       sha1write(f, &crc32_val, 4);
-               }
-
-               /* write the 32-bit offset table */
-               list = sorted_by_sha;
-               for (i = 0; i < nr_written; i++) {
-                       struct object_entry *entry = *list++;
-                       uint32_t offset = (entry->offset <= index_off32_limit) ?
-                               entry->offset : (0x80000000 | nr_large_offset++);
-                       offset = htonl(offset);
-                       sha1write(f, &offset, 4);
-               }
-
-               /* write the large offset table */
-               list = sorted_by_sha;
-               while (nr_large_offset) {
-                       struct object_entry *entry = *list++;
-                       uint64_t offset = entry->offset;
-                       if (offset > index_off32_limit) {
-                               uint32_t split[2];
-                               split[0]        = htonl(offset >> 32);
-                               split[1] = htonl(offset & 0xffffffff);
-                               sha1write(f, split, 8);
-                               nr_large_offset--;
-                       }
-               }
-       }
-
-       sha1write(f, pack_file_sha1, 20);
-       sha1close(f, NULL, 1);
-       SHA1_Final(sha1, &ctx);
-}
-
 static int locate_object_entry_hash(const unsigned char *sha1)
 {
        int i;
@@ -822,7 +704,7 @@ static int locate_object_entry_hash(const unsigned char *sha1)
        memcpy(&ui, sha1, sizeof(unsigned int));
        i = ui % object_ix_hashsz;
        while (0 < object_ix[i]) {
-               if (!hashcmp(sha1, objects[object_ix[i] - 1].sha1))
+               if (!hashcmp(sha1, objects[object_ix[i] - 1].idx.sha1))
                        return i;
                if (++i == object_ix_hashsz)
                        i = 0;
@@ -854,7 +736,7 @@ static void rehash_objects(void)
        object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
        memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
        for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
-               int ix = locate_object_entry_hash(oe->sha1);
+               int ix = locate_object_entry_hash(oe->idx.sha1);
                if (0 <= ix)
                        continue;
                ix = -1 - ix;
@@ -948,7 +830,7 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
 
        entry = objects + nr_objects++;
        memset(entry, 0, sizeof(*entry));
-       hashcpy(entry->sha1, sha1);
+       hashcpy(entry->idx.sha1, sha1);
        entry->hash = hash;
        if (type)
                entry->type = type;
@@ -1268,13 +1150,13 @@ static void check_object(struct object_entry *entry)
                                ofs += 1;
                                if (!ofs || MSB(ofs, 7))
                                        die("delta base offset overflow in pack for %s",
-                                           sha1_to_hex(entry->sha1));
+                                           sha1_to_hex(entry->idx.sha1));
                                c = buf[used_0++];
                                ofs = (ofs << 7) + (c & 127);
                        }
                        if (ofs >= entry->in_pack_offset)
                                die("delta base offset out of bound for %s",
-                                   sha1_to_hex(entry->sha1));
+                                   sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
                        if (!no_reuse_delta && !entry->preferred_base)
                                base_ref = find_packed_object_name(p, ofs);
@@ -1321,10 +1203,10 @@ static void check_object(struct object_entry *entry)
                unuse_pack(&w_curs);
        }
 
-       entry->type = sha1_object_info(entry->sha1, &entry->size);
+       entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
        if (entry->type < 0)
                die("unable to get type of object %s",
-                   sha1_to_hex(entry->sha1));
+                   sha1_to_hex(entry->idx.sha1));
 }
 
 static int pack_offset_sort(const void *_a, const void *_b)
@@ -1334,7 +1216,7 @@ static int pack_offset_sort(const void *_a, const void *_b)
 
        /* avoid filesystem trashing with loose objects */
        if (!a->in_pack && !b->in_pack)
-               return hashcmp(a->sha1, b->sha1);
+               return hashcmp(a->idx.sha1, b->idx.sha1);
 
        if (a->in_pack < b->in_pack)
                return -1;
@@ -1463,16 +1345,16 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
 
        /* Load data if not already done */
        if (!trg->data) {
-               trg->data = read_sha1_file(trg_entry->sha1, &type, &sz);
+               trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
                if (sz != trg_size)
                        die("object %s inconsistent object length (%lu vs %lu)",
-                           sha1_to_hex(trg_entry->sha1), sz, trg_size);
+                           sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
        }
        if (!src->data) {
-               src->data = read_sha1_file(src_entry->sha1, &type, &sz);
+               src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
                if (sz != src_size)
                        die("object %s inconsistent object length (%lu vs %lu)",
-                           sha1_to_hex(src_entry->sha1), sz, src_size);
+                           sha1_to_hex(src_entry->idx.sha1), sz, src_size);
        }
        if (!src->index) {
                src->index = create_delta_index(src->data, src_size);
@@ -1869,12 +1751,12 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                }
                if (!prefixcmp(arg, "--index-version=")) {
                        char *c;
-                       index_default_version = strtoul(arg + 16, &c, 10);
-                       if (index_default_version > 2)
+                       pack_idx_default_version = strtoul(arg + 16, &c, 10);
+                       if (pack_idx_default_version > 2)
                                die("bad %s", arg);
                        if (*c == ',')
-                               index_off32_limit = strtoul(c+1, &c, 0);
-                       if (*c || index_off32_limit & 0x80000000)
+                               pack_idx_off32_limit = strtoul(c+1, &c, 0);
+                       if (*c || pack_idx_off32_limit & 0x80000000)
                                die("bad %s", arg);
                        continue;
                }
index 37addb25fafbedba9bad6e99746ee65bacdee7d3..497903a85a69288afc291ff49e32f7c80140beae 100644 (file)
@@ -352,6 +352,16 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                                : "false");
                                continue;
                        }
+                       if (!strcmp(arg, "--is-inside-work-tree")) {
+                               printf("%s\n", is_inside_work_tree() ? "true"
+                                               : "false");
+                               continue;
+                       }
+                       if (!strcmp(arg, "--is-bare-repository")) {
+                               printf("%s\n", is_bare_repository() ? "true"
+                                               : "false");
+                               continue;
+                       }
                        if (!prefixcmp(arg, "--since=")) {
                                show_datestring("--max-age=", arg+8);
                                continue;
diff --git a/cache.h b/cache.h
index 8a9d1f3883150fd40ac1b6c0a44fa896c3133b04..ae1990a54ea9cd0171e4298af42101b374a932a2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -192,6 +192,7 @@ enum object_type {
 };
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -207,6 +208,7 @@ enum object_type {
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
 extern int is_inside_git_dir(void);
+extern int is_inside_work_tree(void);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
index 8cbda88dda02e7de74149af1b387e537f77bc497..aafa416229df08010203d948923c547eefe3e107 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -589,6 +589,7 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                        unsetenv(ALTERNATE_DB_ENVIRONMENT);
                        unsetenv(DB_ENVIRONMENT);
                        unsetenv(GIT_DIR_ENVIRONMENT);
+                       unsetenv(GIT_WORK_TREE_ENVIRONMENT);
                        unsetenv(GRAFT_ENVIRONMENT);
                        unsetenv(INDEX_ENVIRONMENT);
                        execlp("sh", "sh", "-c", command, NULL);
diff --git a/date.c b/date.c
index a9b59a289e7b22f34958ccc7b80b02a01cf793c6..4690371e5559f72b7755b3cc92ae85843098d285 100644 (file)
--- a/date.c
+++ b/date.c
@@ -414,9 +414,11 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
        num = strtoul(date, &end, 10);
 
        /*
-        * Seconds since 1970? We trigger on that for anything after Jan 1, 2000
+        * Seconds since 1970? We trigger on that for any numbers with
+        * more than 8 digits. This is because we don't want to rule out
+        * numbers like 20070606 as a YYYYMMDD date.
         */
-       if (num > 946684800) {
+       if (num >= 100000000) {
                time_t time = num;
                if (gmtime_r(&time, tm)) {
                        *tm_gmt = 1;
diff --git a/diff.c b/diff.c
index 508bc51ed59998be463fd429ee074654baaad76a..c57ac334141186d23f669bf71c897212a2b37c30 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1107,10 +1107,8 @@ static void setup_diff_attr_check(struct git_attr_check *check)
        check->attr = attr_diff;
 }
 
-#define FIRST_FEW_BYTES 8000
 static int file_is_binary(struct diff_filespec *one)
 {
-       unsigned long sz;
        struct git_attr_check attr_diff_check;
 
        setup_diff_attr_check(&attr_diff_check);
@@ -1127,10 +1125,7 @@ static int file_is_binary(struct diff_filespec *one)
                        return 0;
                diff_populate_filespec(one, 0);
        }
-       sz = one->size;
-       if (FIRST_FEW_BYTES < sz)
-               sz = FIRST_FEW_BYTES;
-       return !!memchr(one->data, 0, sz);
+       return buffer_is_binary(one->data, one->size);
 }
 
 static void builtin_diff(const char *name_a,
index f68afe78a0a0ea4997b8988f241cd3a675d785f9..4e6c9c6cc7c1fbbf69c455d6095cc8d18c81de16 100755 (executable)
@@ -692,8 +692,8 @@ sub commit {
        if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
            # looks like an initial commit
            # use the index primed by git-init
-           $ENV{GIT_INDEX_FILE} = '.git/index';
-           $index{$branch} = '.git/index';
+           $ENV{GIT_INDEX_FILE} = "$git_dir/index";
+           $index{$branch} = "$git_dir/index";
        } else {
            # use an index per branch to speed up
            # imports of projects with many branches
@@ -984,7 +984,7 @@ if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
 }
 
 foreach my $git_index (values %index) {
-    if ($git_index ne '.git/index') {
+    if ($git_index ne "$git_dir/index") {
        unlink($git_index);
     }
 }
index e62351bcbabc00a3d89ddf1c8dd936cea400501f..bb21b037d65f5a63ba461647b449c9390cb1488f 100755 (executable)
@@ -5,7 +5,7 @@
 # Copyright (c) 2006 Theodore Y. Ts'o
 #
 # This file is licensed under the GPL v2, or a later version
-# at the discretion of Junio C Hammano.
+# at the discretion of Junio C Hamano.
 #
 
 USAGE='[--tool=tool] [file to merge] ...'
index f24c7f2d23c13e9874308a019f3c0f93225de3c0..0de49e8459a0d00264de442e6e89db846496541d 100755 (executable)
@@ -29,11 +29,7 @@ set_reflog_action() {
 }
 
 is_bare_repository () {
-       git-config --bool --get core.bare ||
-       case "$GIT_DIR" in
-       .git | */.git) echo false ;;
-       *) echo true ;;
-       esac
+       git-rev-parse --is-bare-repository
 }
 
 cd_to_toplevel () {
@@ -48,7 +44,7 @@ cd_to_toplevel () {
 }
 
 require_work_tree () {
-       test $(is_bare_repository) = false &&
+       test $(git-rev-parse --is-inside-work-tree) = true &&
        test $(git-rev-parse --is-inside-git-dir) = false ||
        die "fatal: $0 cannot be used without a working tree."
 }
index e35006142af766080b3bafe0cdcd39bf2ccf3f04..886b898fccfb37c0473d2d412f97cef6c4a284df 100755 (executable)
@@ -594,8 +594,7 @@ sub post_fetch_checkout {
        my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
        return if -f $index;
 
-       chomp(my $bare = `git config --bool --get core.bare`);
-       return if $bare eq 'true';
+       return if command_oneline(qw/rev-parse --is-inside-work-tree/) eq 'false';
        return if command_oneline(qw/rev-parse --is-inside-git-dir/) eq 'true';
        command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
        print STDERR "Checked out HEAD:\n  ",
index 6f0b7a72192bbeb7ca4693f40f80e2bd20d5eb03..37cee978d2b684c1f0965700bc687802ecc63851 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Copyright (c) 2005 Linus Torvalds
 
-USAGE='-l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
+USAGE='[-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
@@ -13,6 +13,7 @@ message=
 username=
 list=
 verify=
+LINES=0
 while case "$#" in 0) break ;; esac
 do
     case "$1" in
@@ -26,14 +27,41 @@ do
     -f)
        force=1
        ;;
-    -l)
-       case "$#" in
-       1)
-               set x . ;;
+    -n)
+        case $2 in
+       -*)     LINES=1         # no argument
+               ;;
+       *)      shift
+               LINES=$(expr "$1" : '\([0-9]*\)')
+               [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used
+               ;;
        esac
+       ;;
+    -l)
+       list=1
        shift
-       git rev-parse --symbolic --tags | sort | grep "$@"
-       exit $?
+       PATTERN="$1"    # select tags by shell pattern, not re
+       git rev-parse --symbolic --tags | sort |
+           while read TAG
+           do
+               case "$TAG" in
+               *$PATTERN*) ;;
+               *)          continue ;;
+               esac
+               [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;}
+               OBJTYPE=$(git cat-file -t "$TAG")
+               case $OBJTYPE in
+               tag)    ANNOTATION=$(git cat-file tag "$TAG" |
+                                      sed -e '1,/^$/d' \
+                                          -e '/^-----BEGIN PGP SIGNATURE-----$/Q' )
+                       printf "%-15s %s\n" "$TAG" "$ANNOTATION" |
+                         sed -e '2,$s/^/    /' \
+                             -e "${LINES}q"
+                       ;;
+               *)      echo "$TAG"
+                       ;;
+               esac
+           done
        ;;
     -m)
        annotate=1
@@ -97,6 +125,8 @@ do
     shift
 done
 
+[ -n "$list" ] && exit 0
+
 name="$1"
 [ "$name" ] || usage
 prev=0000000000000000000000000000000000000000
diff --git a/git.c b/git.c
index 29b55a16047837084fd9e2e8238137b8a2fe44ea..cd3910afea0cc0e66e720413b2e03d8f00964ff6 100644 (file)
--- a/git.c
+++ b/git.c
@@ -4,7 +4,7 @@
 #include "quote.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
+       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
 static void prepend_to_path(const char *dir, int len)
 {
@@ -69,6 +69,16 @@ static int handle_options(const char*** argv, int* argc)
                        handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+               } else if (!strcmp(cmd, "--work-tree")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No directory given for --work-tree.\n" );
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!prefixcmp(cmd, "--work-tree=")) {
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
                } else if (!strcmp(cmd, "--bare")) {
                        static char git_dir[PATH_MAX+1];
                        setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 1);
@@ -214,7 +224,7 @@ const char git_version_string[] = GIT_VERSION;
  * require working tree to be present -- anything uses this needs
  * RUN_SETUP for reading from the configuration file.
  */
-#define NOT_BARE       (1<<2)
+#define NEED_WORK_TREE (1<<2)
 
 static void handle_internal_command(int argc, const char **argv, char **envp)
 {
@@ -224,7 +234,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                int (*fn)(int, const char **, const char *);
                int option;
        } commands[] = {
-               { "add", cmd_add, RUN_SETUP | NOT_BARE },
+               { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP | USE_PAGER },
                { "apply", cmd_apply },
                { "archive", cmd_archive },
@@ -234,9 +244,9 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
-               { "check-attr", cmd_check_attr, RUN_SETUP | NOT_BARE },
+               { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
                { "cherry", cmd_cherry, RUN_SETUP },
-               { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE },
+               { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
@@ -264,7 +274,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "mailsplit", cmd_mailsplit },
                { "merge-base", cmd_merge_base, RUN_SETUP },
                { "merge-file", cmd_merge_file },
-               { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
+               { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
@@ -277,9 +287,9 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "rerere", cmd_rerere, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rev-parse", cmd_rev_parse, RUN_SETUP },
-               { "revert", cmd_revert, RUN_SETUP | NOT_BARE },
-               { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
-               { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
+               { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
+               { "rm", cmd_rm, RUN_SETUP | NEED_WORK_TREE },
+               { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
@@ -316,8 +326,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                        prefix = setup_git_directory();
                if (p->option & USE_PAGER)
                        setup_pager();
-               if ((p->option & NOT_BARE) &&
-                               (is_bare_repository() || is_inside_git_dir()))
+               if ((p->option & NEED_WORK_TREE) &&
+                               (!is_inside_work_tree() || is_inside_git_dir()))
                        die("%s must be run in a work tree", cmd);
                trace_argv_printf(argv, argc, "trace: built-in: git");
 
index c3921cb0baf162b8294fe47b2fff749114f3061f..e92596c295576d20d76eef23c004e7d2a5db6a3a 100755 (executable)
@@ -94,6 +94,13 @@ our $default_text_plain_charset  = undef;
 # (relative to the current git repository)
 our $mimetypes_file = undef;
 
+# assume this charset if line contains non-UTF-8 characters;
+# it should be valid encoding (see Encoding::Supported(3pm) for list),
+# for which encoding all byte sequences are valid, for example
+# 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it
+# could be even 'utf-8' for the old behavior)
+our $fallback_encoding = 'latin1';
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -602,6 +609,20 @@ sub validate_refname {
        return $input;
 }
 
+# decode sequences of octets in utf8 into Perl's internal form,
+# which is utf-8 with utf8 flag set if needed.  gitweb writes out
+# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
+sub to_utf8 {
+       my $str = shift;
+       my $res;
+       eval { $res = decode_utf8($str, Encode::FB_CROAK); };
+       if (defined $res) {
+               return $res;
+       } else {
+               return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
+       }
+}
+
 # quote unsafe chars, but keep the slash, even when it's not
 # correct, but quoted slashes look too horrible in bookmarks
 sub esc_param {
@@ -626,7 +647,7 @@ sub esc_html ($;%) {
        my $str = shift;
        my %opts = @_;
 
-       $str = decode_utf8($str);
+       $str = to_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
                $str =~ s/ /&nbsp;/g;
@@ -640,7 +661,7 @@ sub esc_path {
        my $str = shift;
        my %opts = @_;
 
-       $str = decode_utf8($str);
+       $str = to_utf8($str);
        $str = $cgi->escapeHTML($str);
        if ($opts{'-nbsp'}) {
                $str =~ s/ /&nbsp;/g;
@@ -925,7 +946,7 @@ sub format_subject_html {
 
        if (length($short) < length($long)) {
                return $cgi->a({-href => $href, -class => "list subject",
-                               -title => decode_utf8($long)},
+                               -title => to_utf8($long)},
                       esc_html($short) . $extra);
        } else {
                return $cgi->a({-href => $href, -class => "list subject"},
@@ -1239,7 +1260,7 @@ sub git_get_projects_list {
                        if (check_export_ok("$projectroot/$path")) {
                                my $pr = {
                                        path => $path,
-                                       owner => decode_utf8($owner),
+                                       owner => to_utf8($owner),
                                };
                                push @list, $pr;
                                (my $forks_path = $path) =~ s/\.git$//;
@@ -1269,7 +1290,7 @@ sub git_get_project_owner {
                        $pr = unescape($pr);
                        $ow = unescape($ow);
                        if ($pr eq $project) {
-                               $owner = decode_utf8($ow);
+                               $owner = to_utf8($ow);
                                last;
                        }
                }
@@ -1759,7 +1780,7 @@ sub get_file_owner {
        }
        my $owner = $gcos;
        $owner =~ s/[,;].*$//;
-       return decode_utf8($owner);
+       return to_utf8($owner);
 }
 
 ## ......................................................................
@@ -1842,7 +1863,7 @@ sub git_header_html {
 
        my $title = "$site_name";
        if (defined $project) {
-               $title .= " - " . decode_utf8($project);
+               $title .= " - " . to_utf8($project);
                if (defined $action) {
                        $title .= "/$action";
                        if (defined $file_name) {
@@ -2116,7 +2137,7 @@ sub git_print_page_path {
 
        print "<div class=\"page_path\">";
        print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
-                     -title => 'tree root'}, decode_utf8("[$project]"));
+                     -title => 'tree root'}, to_utf8("[$project]"));
        print " / ";
        if (defined $name) {
                my @dirname = split '/', $name;
@@ -2936,7 +2957,7 @@ sub git_project_list_body {
                ($pr->{'age'}, $pr->{'age_string'}) = @aa;
                if (!defined $pr->{'descr'}) {
                        my $descr = git_get_project_description($pr->{'path'}) || "";
-                       $pr->{'descr_long'} = decode_utf8($descr);
+                       $pr->{'descr_long'} = to_utf8($descr);
                        $pr->{'descr'} = chop_str($descr, 25, 5);
                }
                if (!defined $pr->{'owner'}) {
@@ -3981,7 +4002,7 @@ sub git_snapshot {
        my $git = git_cmd_str();
        my $name = $project;
        $name =~ s/\047/\047\\\047\047/g;
-       my $filename = decode_utf8(basename($project));
+       my $filename = to_utf8(basename($project));
        my $cmd;
        if ($suffix eq 'zip') {
                $filename .= "-$hash.$suffix";
diff --git a/grep.c b/grep.c
index fcc676230282e4c5bda4f9f97c5ae65c1a6beb30..f67d6716ea5f42c3384a7a4cb2eb973b02785fba 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "grep.h"
+#include "xdiff-interface.h"
 
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
                         const char *origin, int no, enum grep_pat_token t)
@@ -232,17 +233,6 @@ static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
        printf("%.*s\n", (int)(eol-bol), bol);
 }
 
-/*
- * NEEDSWORK: share code with diff.c
- */
-#define FIRST_FEW_BYTES 8000
-static int buffer_is_binary(const char *ptr, unsigned long size)
-{
-       if (FIRST_FEW_BYTES < size)
-               size = FIRST_FEW_BYTES;
-       return !!memchr(ptr, 0, size);
-}
-
 static int fixmatch(const char *pattern, char *line, regmatch_t *match)
 {
        char *hit = strstr(line, pattern);
diff --git a/ident.c b/ident.c
index 69a04b827d7805d9b29153e31e5489edb587690a..3d49608e6f9afe22f24a2faf70f2d6cee50b8919 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -195,10 +195,10 @@ const char *fmt_ident(const char *name, const char *email,
        setup_ident();
        if (!name)
                name = git_default_name;
-       if (!email)
-               email = getenv("EMAIL");
        if (!email)
                email = git_default_email;
+       if (!email)
+               email = getenv("EMAIL");
 
        if (!*name) {
                struct passwd *pw;
index 58c4a9c41dd7a05b86d40e6eeee33ba0a3fb6c4f..82c8da3683bbda15a5f7476d93c14737617d3e49 100644 (file)
@@ -13,13 +13,11 @@ static const char index_pack_usage[] =
 
 struct object_entry
 {
-       off_t offset;
+       struct pack_idx_entry idx;
        unsigned long size;
        unsigned int hdr_size;
-       uint32_t crc32;
        enum object_type type;
        enum object_type real_type;
-       unsigned char sha1[20];
 };
 
 union delta_base {
@@ -197,7 +195,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
        unsigned shift;
        void *data;
 
-       obj->offset = consumed_bytes;
+       obj->idx.offset = consumed_bytes;
        input_crc32 = crc32(0, Z_NULL, 0);
 
        p = fill(1);
@@ -229,15 +227,15 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                while (c & 128) {
                        base_offset += 1;
                        if (!base_offset || MSB(base_offset, 7))
-                               bad_object(obj->offset, "offset value overflow for delta base object");
+                               bad_object(obj->idx.offset, "offset value overflow for delta base object");
                        p = fill(1);
                        c = *p;
                        use(1);
                        base_offset = (base_offset << 7) + (c & 127);
                }
-               delta_base->offset = obj->offset - base_offset;
-               if (delta_base->offset >= obj->offset)
-                       bad_object(obj->offset, "delta base offset is out of bound");
+               delta_base->offset = obj->idx.offset - base_offset;
+               if (delta_base->offset >= obj->idx.offset)
+                       bad_object(obj->idx.offset, "delta base offset is out of bound");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@ -245,19 +243,19 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
        case OBJ_TAG:
                break;
        default:
-               bad_object(obj->offset, "unknown object type %d", obj->type);
+               bad_object(obj->idx.offset, "unknown object type %d", obj->type);
        }
-       obj->hdr_size = consumed_bytes - obj->offset;
+       obj->hdr_size = consumed_bytes - obj->idx.offset;
 
-       data = unpack_entry_data(obj->offset, obj->size);
-       obj->crc32 = input_crc32;
+       data = unpack_entry_data(obj->idx.offset, obj->size);
+       obj->idx.crc32 = input_crc32;
        return data;
 }
 
 static void *get_data_from_pack(struct object_entry *obj)
 {
-       unsigned long from = obj[0].offset + obj[0].hdr_size;
-       unsigned long len = obj[1].offset - from;
+       unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
+       unsigned long len = obj[1].idx.offset - from;
        unsigned long rdy = 0;
        unsigned char *src, *data;
        z_stream stream;
@@ -360,11 +358,11 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
                             &result_size);
        free(delta_data);
        if (!result)
-               bad_object(delta_obj->offset, "failed to apply delta");
-       sha1_object(result, result_size, type, delta_obj->sha1);
+               bad_object(delta_obj->idx.offset, "failed to apply delta");
+       sha1_object(result, result_size, type, delta_obj->idx.sha1);
        nr_resolved_deltas++;
 
-       hashcpy(delta_base.sha1, delta_obj->sha1);
+       hashcpy(delta_base.sha1, delta_obj->idx.sha1);
        if (!find_delta_children(&delta_base, &first, &last)) {
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
@@ -374,7 +372,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
        }
 
        memset(&delta_base, 0, sizeof(delta_base));
-       delta_base.offset = delta_obj->offset;
+       delta_base.offset = delta_obj->idx.offset;
        if (!find_delta_children(&delta_base, &first, &last)) {
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
@@ -418,12 +416,12 @@ static void parse_pack_objects(unsigned char *sha1)
                        delta->obj_no = i;
                        delta++;
                } else
-                       sha1_object(data, obj->size, obj->type, obj->sha1);
+                       sha1_object(data, obj->size, obj->type, obj->idx.sha1);
                free(data);
                if (verbose)
                        display_progress(&progress, i+1);
        }
-       objects[i].offset = consumed_bytes;
+       objects[i].idx.offset = consumed_bytes;
        if (verbose)
                stop_progress(&progress);
 
@@ -465,10 +463,10 @@ static void parse_pack_objects(unsigned char *sha1)
 
                if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
                        continue;
-               hashcpy(base.sha1, obj->sha1);
+               hashcpy(base.sha1, obj->idx.sha1);
                ref = !find_delta_children(&base, &ref_first, &ref_last);
                memset(&base, 0, sizeof(base));
-               base.offset = obj->offset;
+               base.offset = obj->idx.offset;
                ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
                if (!ref && !ofs)
                        continue;
@@ -535,11 +533,11 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
        }
        header[n++] = c;
        write_or_die(output_fd, header, n);
-       obj[0].crc32 = crc32(0, Z_NULL, 0);
-       obj[0].crc32 = crc32(obj[0].crc32, header, n);
-       obj[1].offset = obj[0].offset + n;
-       obj[1].offset += write_compressed(output_fd, buf, size, &obj[0].crc32);
-       hashcpy(obj->sha1, sha1);
+       obj[0].idx.crc32 = crc32(0, Z_NULL, 0);
+       obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n);
+       obj[1].idx.offset = obj[0].idx.offset + n;
+       obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
+       hashcpy(obj->idx.sha1, sha1);
 }
 
 static int delta_pos_compare(const void *_a, const void *_b)
@@ -602,145 +600,6 @@ static void fix_unresolved_deltas(int nr_unresolved)
        free(sorted_by_pos);
 }
 
-static uint32_t index_default_version = 1;
-static uint32_t index_off32_limit = 0x7fffffff;
-
-static int sha1_compare(const void *_a, const void *_b)
-{
-       struct object_entry *a = *(struct object_entry **)_a;
-       struct object_entry *b = *(struct object_entry **)_b;
-       return hashcmp(a->sha1, b->sha1);
-}
-
-/*
- * On entry *sha1 contains the pack content SHA1 hash, on exit it is
- * the SHA1 hash of sorted object names.
- */
-static const char *write_index_file(const char *index_name, unsigned char *sha1)
-{
-       struct sha1file *f;
-       struct object_entry **sorted_by_sha, **list, **last;
-       uint32_t array[256];
-       int i, fd;
-       SHA_CTX ctx;
-       uint32_t index_version;
-
-       if (nr_objects) {
-               sorted_by_sha =
-                       xcalloc(nr_objects, sizeof(struct object_entry *));
-               list = sorted_by_sha;
-               last = sorted_by_sha + nr_objects;
-               for (i = 0; i < nr_objects; ++i)
-                       sorted_by_sha[i] = &objects[i];
-               qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
-                     sha1_compare);
-       }
-       else
-               sorted_by_sha = list = last = NULL;
-
-       if (!index_name) {
-               static char tmpfile[PATH_MAX];
-               snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/tmp_idx_XXXXXX", get_object_directory());
-               fd = mkstemp(tmpfile);
-               index_name = xstrdup(tmpfile);
-       } else {
-               unlink(index_name);
-               fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
-       }
-       if (fd < 0)
-               die("unable to create %s: %s", index_name, strerror(errno));
-       f = sha1fd(fd, index_name);
-
-       /* if last object's offset is >= 2^31 we should use index V2 */
-       index_version = (objects[nr_objects-1].offset >> 31) ? 2 : index_default_version;
-
-       /* index versions 2 and above need a header */
-       if (index_version >= 2) {
-               struct pack_idx_header hdr;
-               hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
-               hdr.idx_version = htonl(index_version);
-               sha1write(f, &hdr, sizeof(hdr));
-       }
-
-       /*
-        * Write the first-level table (the list is sorted,
-        * but we use a 256-entry lookup to be able to avoid
-        * having to do eight extra binary search iterations).
-        */
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = list;
-               while (next < last) {
-                       struct object_entry *obj = *next;
-                       if (obj->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - sorted_by_sha);
-               list = next;
-       }
-       sha1write(f, array, 256 * 4);
-
-       /* compute the SHA1 hash of sorted object names. */
-       SHA1_Init(&ctx);
-
-       /*
-        * Write the actual SHA1 entries..
-        */
-       list = sorted_by_sha;
-       for (i = 0; i < nr_objects; i++) {
-               struct object_entry *obj = *list++;
-               if (index_version < 2) {
-                       uint32_t offset = htonl(obj->offset);
-                       sha1write(f, &offset, 4);
-               }
-               sha1write(f, obj->sha1, 20);
-               SHA1_Update(&ctx, obj->sha1, 20);
-       }
-
-       if (index_version >= 2) {
-               unsigned int nr_large_offset = 0;
-
-               /* write the crc32 table */
-               list = sorted_by_sha;
-               for (i = 0; i < nr_objects; i++) {
-                       struct object_entry *obj = *list++;
-                       uint32_t crc32_val = htonl(obj->crc32);
-                       sha1write(f, &crc32_val, 4);
-               }
-
-               /* write the 32-bit offset table */
-               list = sorted_by_sha;
-               for (i = 0; i < nr_objects; i++) {
-                       struct object_entry *obj = *list++;
-                       uint32_t offset = (obj->offset <= index_off32_limit) ?
-                               obj->offset : (0x80000000 | nr_large_offset++);
-                       offset = htonl(offset);
-                       sha1write(f, &offset, 4);
-               }
-
-               /* write the large offset table */
-               list = sorted_by_sha;
-               while (nr_large_offset) {
-                       struct object_entry *obj = *list++;
-                       uint64_t offset = obj->offset;
-                       if (offset > index_off32_limit) {
-                               uint32_t split[2];
-                               split[0]        = htonl(offset >> 32);
-                               split[1] = htonl(offset & 0xffffffff);
-                               sha1write(f, split, 8);
-                               nr_large_offset--;
-                       }
-               }
-       }
-
-       sha1write(f, sha1, 20);
-       sha1close(f, NULL, 1);
-       free(sorted_by_sha);
-       SHA1_Final(sha1, &ctx);
-       return index_name;
-}
-
 static void final(const char *final_pack_name, const char *curr_pack_name,
                  const char *final_index_name, const char *curr_index_name,
                  const char *keep_name, const char *keep_msg,
@@ -830,6 +689,7 @@ int main(int argc, char **argv)
        const char *curr_index, *index_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
        char *index_name_buf = NULL, *keep_name_buf = NULL;
+       struct pack_idx_entry **idx_objects;
        unsigned char sha1[20];
 
        for (i = 1; i < argc; i++) {
@@ -865,12 +725,12 @@ int main(int argc, char **argv)
                                index_name = argv[++i];
                        } else if (!prefixcmp(arg, "--index-version=")) {
                                char *c;
-                               index_default_version = strtoul(arg + 16, &c, 10);
-                               if (index_default_version > 2)
+                               pack_idx_default_version = strtoul(arg + 16, &c, 10);
+                               if (pack_idx_default_version > 2)
                                        die("bad %s", arg);
                                if (*c == ',')
-                                       index_off32_limit = strtoul(c+1, &c, 0);
-                               if (*c || index_off32_limit & 0x80000000)
+                                       pack_idx_off32_limit = strtoul(c+1, &c, 0);
+                               if (*c || pack_idx_off32_limit & 0x80000000)
                                        die("bad %s", arg);
                        } else
                                usage(index_pack_usage);
@@ -940,7 +800,13 @@ int main(int argc, char **argv)
                            nr_deltas - nr_resolved_deltas);
        }
        free(deltas);
-       curr_index = write_index_file(index_name, sha1);
+
+       idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
+       for (i = 0; i < nr_objects; i++)
+               idx_objects[i] = &objects[i].idx;
+       curr_index = write_idx_file(index_name, idx_objects, nr_objects, sha1);
+       free(idx_objects);
+
        final(pack_name, curr_pack,
                index_name, curr_index,
                keep_name, keep_msg,
index 8f72b2c079a68ac5cb9d1c90bb7b72fa779efb5f..4a82b741ae020376ac67b34d5fce86e8a87a3b5d 100644 (file)
@@ -680,6 +680,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 {
        xpparam_t xpp;
 
+       if (buffer_is_binary(orig->ptr, orig->size) ||
+                       buffer_is_binary(src1->ptr, src1->size) ||
+                       buffer_is_binary(src2->ptr, src2->size))
+               return error("Cannot merge binary files: %s vs. %s\n",
+                       name1, name2);
+
        memset(&xpp, 0, sizeof(xpp));
        return xdl_merge(orig,
                         src1, name1,
index cfc4969ed9ba0ccfdba8f97637bac20be31d1eba..16793d9958a57664233b9e4468e112dfa1a8a915 100644 (file)
--- a/object.c
+++ b/object.c
@@ -160,8 +160,11 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                parse_tag_buffer(tag, buffer, size);
                obj = &tag->object;
        } else {
+               warning("object %s has unknown type id %d\n", sha1_to_hex(sha1), type);
                obj = NULL;
        }
+       if (obj && obj->type == OBJ_NONE)
+               obj->type = type;
        *eaten_p = eaten;
        return obj;
 }
index ae2e481e5554cfa66cf137d38c38f961b9aa8ec7..1cf5f7c9f0956a457de03c64238d298a9d996984 100644 (file)
@@ -1,5 +1,147 @@
 #include "cache.h"
 #include "pack.h"
+#include "csum-file.h"
+
+uint32_t pack_idx_default_version = 1;
+uint32_t pack_idx_off32_limit = 0x7fffffff;
+
+static int sha1_compare(const void *_a, const void *_b)
+{
+       struct pack_idx_entry *a = *(struct pack_idx_entry **)_a;
+       struct pack_idx_entry *b = *(struct pack_idx_entry **)_b;
+       return hashcmp(a->sha1, b->sha1);
+}
+
+/*
+ * On entry *sha1 contains the pack content SHA1 hash, on exit it is
+ * the SHA1 hash of sorted object names. The objects array passed in
+ * will be sorted by SHA1 on exit.
+ */
+const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1)
+{
+       struct sha1file *f;
+       struct pack_idx_entry **sorted_by_sha, **list, **last;
+       off_t last_obj_offset = 0;
+       uint32_t array[256];
+       int i, fd;
+       SHA_CTX ctx;
+       uint32_t index_version;
+
+       if (nr_objects) {
+               sorted_by_sha = objects;
+               list = sorted_by_sha;
+               last = sorted_by_sha + nr_objects;
+               for (i = 0; i < nr_objects; ++i) {
+                       if (objects[i]->offset > last_obj_offset)
+                               last_obj_offset = objects[i]->offset;
+               }
+               qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
+                     sha1_compare);
+       }
+       else
+               sorted_by_sha = list = last = NULL;
+
+       if (!index_name) {
+               static char tmpfile[PATH_MAX];
+               snprintf(tmpfile, sizeof(tmpfile),
+                        "%s/tmp_idx_XXXXXX", get_object_directory());
+               fd = mkstemp(tmpfile);
+               index_name = xstrdup(tmpfile);
+       } else {
+               unlink(index_name);
+               fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+       }
+       if (fd < 0)
+               die("unable to create %s: %s", index_name, strerror(errno));
+       f = sha1fd(fd, index_name);
+
+       /* if last object's offset is >= 2^31 we should use index V2 */
+       index_version = (last_obj_offset >> 31) ? 2 : pack_idx_default_version;
+
+       /* index versions 2 and above need a header */
+       if (index_version >= 2) {
+               struct pack_idx_header hdr;
+               hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+               hdr.idx_version = htonl(index_version);
+               sha1write(f, &hdr, sizeof(hdr));
+       }
+
+       /*
+        * Write the first-level table (the list is sorted,
+        * but we use a 256-entry lookup to be able to avoid
+        * having to do eight extra binary search iterations).
+        */
+       for (i = 0; i < 256; i++) {
+               struct pack_idx_entry **next = list;
+               while (next < last) {
+                       struct pack_idx_entry *obj = *next;
+                       if (obj->sha1[0] != i)
+                               break;
+                       next++;
+               }
+               array[i] = htonl(next - sorted_by_sha);
+               list = next;
+       }
+       sha1write(f, array, 256 * 4);
+
+       /* compute the SHA1 hash of sorted object names. */
+       SHA1_Init(&ctx);
+
+       /*
+        * Write the actual SHA1 entries..
+        */
+       list = sorted_by_sha;
+       for (i = 0; i < nr_objects; i++) {
+               struct pack_idx_entry *obj = *list++;
+               if (index_version < 2) {
+                       uint32_t offset = htonl(obj->offset);
+                       sha1write(f, &offset, 4);
+               }
+               sha1write(f, obj->sha1, 20);
+               SHA1_Update(&ctx, obj->sha1, 20);
+       }
+
+       if (index_version >= 2) {
+               unsigned int nr_large_offset = 0;
+
+               /* write the crc32 table */
+               list = sorted_by_sha;
+               for (i = 0; i < nr_objects; i++) {
+                       struct pack_idx_entry *obj = *list++;
+                       uint32_t crc32_val = htonl(obj->crc32);
+                       sha1write(f, &crc32_val, 4);
+               }
+
+               /* write the 32-bit offset table */
+               list = sorted_by_sha;
+               for (i = 0; i < nr_objects; i++) {
+                       struct pack_idx_entry *obj = *list++;
+                       uint32_t offset = (obj->offset <= pack_idx_off32_limit) ?
+                               obj->offset : (0x80000000 | nr_large_offset++);
+                       offset = htonl(offset);
+                       sha1write(f, &offset, 4);
+               }
+
+               /* write the large offset table */
+               list = sorted_by_sha;
+               while (nr_large_offset) {
+                       struct pack_idx_entry *obj = *list++;
+                       uint64_t offset = obj->offset;
+                       if (offset > pack_idx_off32_limit) {
+                               uint32_t split[2];
+                               split[0] = htonl(offset >> 32);
+                               split[1] = htonl(offset & 0xffffffff);
+                               sha1write(f, split, 8);
+                               nr_large_offset--;
+                       }
+               }
+       }
+
+       sha1write(f, sha1, 20);
+       sha1close(f, NULL, 1);
+       SHA1_Final(sha1, &ctx);
+       return index_name;
+}
 
 void fixup_pack_header_footer(int pack_fd,
                         unsigned char *pack_file_sha1,
diff --git a/pack.h b/pack.h
index d667fb8d5a49b4480beaa6f384f1f6f86b035806..f357c9f4282d5bc8bbcff6f3a44b9812415745a6 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -34,6 +34,10 @@ struct pack_header {
  */
 #define PACK_IDX_SIGNATURE 0xff744f63  /* "\377tOc" */
 
+/* These may be overridden by command-line parameters */
+extern uint32_t pack_idx_default_version;
+extern uint32_t pack_idx_off32_limit;
+
 /*
  * Packed object index header
  */
@@ -42,6 +46,16 @@ struct pack_idx_header {
        uint32_t idx_version;
 };
 
+/*
+ * Common part of object structure used for write_idx_file
+ */
+struct pack_idx_entry {
+       unsigned char sha1[20];
+       uint32_t crc32;
+       off_t offset;
+};
+
+extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
 
 extern int verify_pack(struct packed_git *, int);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
diff --git a/setup.c b/setup.c
index a45ea8309a9773160597f142f1208a6129885499..dba8012659cf85fa96cad4fcbc55e377c7e876b9 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -95,7 +95,7 @@ void verify_non_filename(const char *prefix, const char *arg)
        const char *name;
        struct stat st;
 
-       if (is_inside_git_dir())
+       if (!is_inside_work_tree() || is_inside_git_dir())
                return;
        if (*arg == '-')
                return; /* flag */
@@ -174,41 +174,96 @@ static int inside_git_dir = -1;
 
 int is_inside_git_dir(void)
 {
-       if (inside_git_dir < 0) {
-               char buffer[1024];
-
-               if (is_bare_repository())
-                       return (inside_git_dir = 1);
-               if (getcwd(buffer, sizeof(buffer))) {
-                       const char *git_dir = get_git_dir(), *cwd = buffer;
-                       while (*git_dir && *git_dir == *cwd) {
-                               git_dir++;
-                               cwd++;
-                       }
-                       inside_git_dir = !*git_dir;
-               } else
-                       inside_git_dir = 0;
+       if (inside_git_dir >= 0)
+               return inside_git_dir;
+       die("BUG: is_inside_git_dir called before setup_git_directory");
+}
+
+static int inside_work_tree = -1;
+
+int is_inside_work_tree(void)
+{
+       if (inside_git_dir >= 0)
+               return inside_work_tree;
+       die("BUG: is_inside_work_tree called before setup_git_directory");
+}
+
+static char *gitworktree_config;
+
+static int git_setup_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "core.worktree")) {
+               if (gitworktree_config)
+                       strlcpy(gitworktree_config, value, PATH_MAX);
+               return 0;
        }
-       return inside_git_dir;
+       return git_default_config(var, value);
 }
 
 const char *setup_git_directory_gently(int *nongit_ok)
 {
        static char cwd[PATH_MAX+1];
-       const char *gitdirenv;
-       int len, offset;
+       char worktree[PATH_MAX+1], gitdir[PATH_MAX+1];
+       const char *gitdirenv, *gitworktree;
+       int wt_rel_gitdir = 0;
 
-       /*
-        * If GIT_DIR is set explicitly, we're not going
-        * to do any discovery, but we still do repository
-        * validation.
-        */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv) {
-               if (PATH_MAX - 40 < strlen(gitdirenv))
-                       die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-               if (is_git_directory(gitdirenv))
+       if (!gitdirenv) {
+               int len, offset;
+
+               if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+                       die("Unable to read current working directory");
+
+               offset = len = strlen(cwd);
+               for (;;) {
+                       if (is_git_directory(".git"))
+                               break;
+                       if (offset == 0) {
+                               offset = -1;
+                               break;
+                       }
+                       chdir("..");
+                       while (cwd[--offset] != '/')
+                               ; /* do nothing */
+               }
+
+               if (offset >= 0) {
+                       inside_work_tree = 1;
+                       git_config(git_default_config);
+                       if (offset == len) {
+                               inside_git_dir = 0;
+                               return NULL;
+                       }
+
+                       cwd[len++] = '/';
+                       cwd[len] = '\0';
+                       inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/");
+                       return cwd + offset + 1;
+               }
+
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
+               if (!is_git_directory(".")) {
+                       if (nongit_ok) {
+                               *nongit_ok = 1;
+                               return NULL;
+                       }
+                       die("Not a git repository");
+               }
+               setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
+               gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+               if (!gitdirenv)
+                       die("getenv after setenv failed");
+       }
+
+       if (PATH_MAX - 40 < strlen(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
                        return NULL;
+               }
+               die("$%s too big", GIT_DIR_ENVIRONMENT);
+       }
+       if (!is_git_directory(gitdirenv)) {
                if (nongit_ok) {
                        *nongit_ok = 1;
                        return NULL;
@@ -218,41 +273,92 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
        if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
                die("Unable to read current working directory");
+       if (chdir(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       return NULL;
+               }
+               die("Cannot change directory to $%s '%s'",
+                       GIT_DIR_ENVIRONMENT, gitdirenv);
+       }
+       if (!getcwd(gitdir, sizeof(gitdir)-1) || gitdir[0] != '/')
+               die("Unable to read current working directory");
+       if (chdir(cwd))
+               die("Cannot come back to cwd");
 
-       offset = len = strlen(cwd);
-       for (;;) {
-               if (is_git_directory(".git"))
-                       break;
-               chdir("..");
-               do {
-                       if (!offset) {
-                               if (is_git_directory(cwd)) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
-                                       inside_git_dir = 1;
-                                       return NULL;
-                               }
-                               if (nongit_ok) {
-                                       if (chdir(cwd))
-                                               die("Cannot come back to cwd");
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               die("Not a git repository");
+       /*
+        * In case there is a work tree we may change the directory,
+        * therefore make GIT_DIR an absolute path.
+        */
+       if (gitdirenv[0] != '/') {
+               setenv(GIT_DIR_ENVIRONMENT, gitdir, 1);
+               gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+               if (!gitdirenv)
+                       die("getenv after setenv failed");
+               if (PATH_MAX - 40 < strlen(gitdirenv)) {
+                       if (nongit_ok) {
+                               *nongit_ok = 1;
+                               return NULL;
                        }
-               } while (cwd[--offset] != '/');
+                       die("$%s too big after expansion to absolute path",
+                               GIT_DIR_ENVIRONMENT);
+               }
+       }
+
+       strcat(cwd, "/");
+       strcat(gitdir, "/");
+       inside_git_dir = !prefixcmp(cwd, gitdir);
+
+       gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);
+       if (!gitworktree) {
+               gitworktree_config = worktree;
+               worktree[0] = '\0';
+       }
+       git_config(git_setup_config);
+       if (!gitworktree) {
+               gitworktree_config = NULL;
+               if (worktree[0])
+                       gitworktree = worktree;
+               if (gitworktree && gitworktree[0] != '/')
+                       wt_rel_gitdir = 1;
+       }
+
+       if (wt_rel_gitdir && chdir(gitdirenv))
+               die("Cannot change directory to $%s '%s'",
+                       GIT_DIR_ENVIRONMENT, gitdirenv);
+       if (gitworktree && chdir(gitworktree)) {
+               if (nongit_ok) {
+                       if (wt_rel_gitdir && chdir(cwd))
+                               die("Cannot come back to cwd");
+                       *nongit_ok = 1;
+                       return NULL;
+               }
+               if (wt_rel_gitdir)
+                       die("Cannot change directory to working tree '%s'"
+                               " from $%s", gitworktree, GIT_DIR_ENVIRONMENT);
+               else
+                       die("Cannot change directory to working tree '%s'",
+                               gitworktree);
        }
+       if (!getcwd(worktree, sizeof(worktree)-1) || worktree[0] != '/')
+               die("Unable to read current working directory");
+       strcat(worktree, "/");
+       inside_work_tree = !prefixcmp(cwd, worktree);
 
-       if (offset == len)
+       if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&
+           strcmp(worktree, gitdir)) {
+               inside_git_dir = 0;
+       }
+
+       if (!inside_work_tree) {
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
                return NULL;
+       }
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
-       cwd[len++] = '/';
-       cwd[len] = 0;
-       inside_git_dir = !prefixcmp(cwd + offset, ".git/");
-       return cwd + offset;
+       if (!strcmp(cwd, worktree))
+               return NULL;
+       return cwd+strlen(worktree);
 }
 
 int git_config_perm(const char *var, const char *value)
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
new file mode 100755 (executable)
index 0000000..ec49966
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='test git rev-parse'
+. ./test-lib.sh
+
+test_rev_parse() {
+       name=$1
+       shift
+
+       test_expect_success "$name: is-bare-repository" \
+       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-git-dir" \
+       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-work-tree" \
+       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: prefix" \
+       "test '$1' = \"\$(git rev-parse --show-prefix)\""
+       shift
+       [ $# -eq 0 ] && return
+}
+
+test_rev_parse toplevel false false true ''
+
+cd .git || exit 1
+test_rev_parse .git/ false true true .git/
+cd objects || exit 1
+test_rev_parse .git/objects/ false true true .git/objects/
+cd ../.. || exit 1
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+test_rev_parse subdirectory false false true sub/dir/
+cd ../.. || exit 1
+
+git config core.bare true
+test_rev_parse 'core.bare = true' true false true
+
+git config --unset core.bare
+test_rev_parse 'core.bare undefined' false false true
+
+mkdir work || exit 1
+cd work || exit 1
+export GIT_DIR=../.git
+export GIT_CONFIG="$GIT_DIR"/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false true ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+
+mv ../.git ../repo.git || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG="$GIT_DIR"/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false true ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' true false true ''
+
+test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
new file mode 100755 (executable)
index 0000000..aadeeab
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='test separate work tree'
+. ./test-lib.sh
+
+test_rev_parse() {
+       name=$1
+       shift
+
+       test_expect_success "$name: is-bare-repository" \
+       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-git-dir" \
+       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: is-inside-work-tree" \
+       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+       shift
+       [ $# -eq 0 ] && return
+
+       test_expect_success "$name: prefix" \
+       "test '$1' = \"\$(git rev-parse --show-prefix)\""
+       shift
+       [ $# -eq 0 ] && return
+}
+
+mkdir -p work/sub/dir || exit 1
+mv .git repo.git || exit 1
+
+say "core.worktree = relative path"
+export GIT_DIR=repo.git
+export GIT_CONFIG=$GIT_DIR/config
+unset GIT_WORK_TREE
+git config core.worktree ../work
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+export GIT_DIR=../repo.git
+export GIT_CONFIG=$GIT_DIR/config
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+export GIT_DIR=../../../repo.git
+export GIT_CONFIG=$GIT_DIR/config
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "core.worktree = absolute path"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree "$(pwd)/work"
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "GIT_WORK_TREE=relative path (override core.worktree)"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+git config core.worktree non-existent
+export GIT_WORK_TREE=work
+test_rev_parse 'outside'      false false false
+cd work || exit 1
+export GIT_WORK_TREE=.
+test_rev_parse 'inside'       false false true ''
+cd sub/dir || exit 1
+export GIT_WORK_TREE=../..
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+mv work repo.git/work
+
+say "GIT_WORK_TREE=absolute path, work tree below git dir"
+export GIT_DIR=$(pwd)/repo.git
+export GIT_CONFIG=$GIT_DIR/config
+export GIT_WORK_TREE=$(pwd)/repo.git/work
+test_rev_parse 'outside'              false false false
+cd repo.git || exit 1
+test_rev_parse 'in repo.git'              false true  false
+cd objects || exit 1
+test_rev_parse 'in repo.git/objects'      false true  false
+cd ../work || exit 1
+test_rev_parse 'in repo.git/work'         false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'in repo.git/sub/dir' false false true sub/dir/
+cd ../../../.. || exit 1
+
+test_done
index 828d553a4b53210e9bd15c522e2a798901b2ac35..6f6d8844e8628d006b1d4c11eb02a5ee14a8fd35 100755 (executable)
@@ -136,8 +136,8 @@ test_expect_success 'test tracking setup (non-wildcard, not matching)' \
      git-config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --track my5 local/master &&
-     ! test $(git-config branch.my5.remote) = local &&
-     ! test $(git-config branch.my5.merge) = refs/heads/master'
+     ! test "$(git-config branch.my5.remote)" = local &&
+     ! test "$(git-config branch.my5.merge)" = refs/heads/master'
 
 test_expect_success 'test tracking setup via config' \
     'git-config branch.autosetupmerge true &&
@@ -155,14 +155,22 @@ test_expect_success 'test overriding tracking setup via --no-track' \
      (git-show-ref -q refs/remotes/local/master || git-fetch local) &&
      git-branch --no-track my2 local/master &&
      git-config branch.autosetupmerge false &&
-     ! test $(git-config branch.my2.remote) = local &&
-     ! test $(git-config branch.my2.merge) = refs/heads/master'
+     ! test "$(git-config branch.my2.remote)" = local &&
+     ! test "$(git-config branch.my2.merge)" = refs/heads/master'
 
 test_expect_success 'test local tracking setup' \
     'git branch --track my6 s &&
      test $(git-config branch.my6.remote) = . &&
      test $(git-config branch.my6.merge) = refs/heads/s'
 
+test_expect_success 'test tracking setup via --track but deeper' \
+    'git-config remote.local.url . &&
+     git-config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+     (git-show-ref -q refs/remotes/local/o/o || git-fetch local) &&
+     git-branch --track my7 local/o/o &&
+     test "$(git-config branch.my7.remote)" = local &&
+     test "$(git-config branch.my7.merge)" = refs/heads/o/o'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000     branch: Created from master
index e223c074f043571ff868a2f4783c7cc155185004..5500505d8b86fad4d69fe3bca69b8d023737e79e 100755 (executable)
@@ -108,6 +108,13 @@ test_expect_success \
     'git-archive --format=zip' \
     'git-archive --format=zip HEAD >d.zip'
 
+$UNZIP -v 2>/dev/null
+if [ $? -eq 127 ]; then
+       echo "Skipping ZIP tests, because unzip was not found"
+       test_done
+       exit
+fi
+
 test_expect_success \
     'extract ZIP archive' \
     '(mkdir d && cd d && $UNZIP ../d.zip)'
index a398556137205d9fb561746335a13330a5aeda54..058db9cc52521a9ba9e408c9455645d8adc4ba79 100755 (executable)
@@ -81,4 +81,18 @@ EOF
 
 test_expect_success "virtual trees were processed" "git diff expect out"
 
+git reset --hard
+test_expect_success 'refuse to merge binary files' '
+       printf "\0" > binary-file &&
+       git add binary-file &&
+       git commit -m binary &&
+       git checkout G &&
+       printf "\0\0" > binary-file &&
+       git add binary-file &&
+       git commit -m binary2 &&
+       ! git merge F > merge.out 2> merge.err &&
+       grep "Cannot merge binary files: HEAD:binary-file vs. F:binary-file" \
+               merge.err
+'
+
 test_done
index b92ab6331204ca40dd51b618623e5241ae08dd37..44ae503b992ee26023a19830039062e01be9aa57 100755 (executable)
@@ -487,4 +487,32 @@ test_expect_success \
        'gitweb_run "p=.git;a=atom"'
 test_debug 'cat gitweb.log'
 
+# ----------------------------------------------------------------------
+# encoding/decoding
+
+test_expect_success \
+       'encode(commit): utf8' \
+       '. ../t3901-utf8.txt &&
+        echo "UTF-8" >> file &&
+        git add file &&
+        git commit -F ../t3900/1-UTF-8.txt &&
+        gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'encode(commit): iso-8859-1' \
+       '. ../t3901-8859-1.txt &&
+        echo "ISO-8859-1" >> file &&
+        git add file &&
+        git config i18n.commitencoding ISO-8859-1 &&
+        git commit -F ../t3900/ISO-8859-1.txt &&
+        git config --unset i18n.commitencoding &&
+        gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+       'encode(log): utf-8 and iso-8859-1' \
+       'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
 test_done
index dee3ad76212ea95c75c8287c8d8c0f312125bd06..b61e1d598de606a496cbc93345600bf8b380f426 100644 (file)
@@ -26,6 +26,7 @@ GIT_COMMITTER_EMAIL=committer@example.com
 GIT_COMMITTER_NAME='C O Mitter'
 unset GIT_DIFF_OPTS
 unset GIT_DIR
+unset GIT_WORK_TREE
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
diff --git a/tree.c b/tree.c
index a3728270b4027cd56e09e1a56ec17e177d0a6998..04fe653a8e25feb0fc15f76ba487d0f8dbd52f82 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -173,8 +173,13 @@ static void track_tree_refs(struct tree *item)
                        continue;
                if (S_ISDIR(entry.mode))
                        obj = &lookup_tree(entry.sha1)->object;
-               else
+               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
                        obj = &lookup_blob(entry.sha1)->object;
+               else {
+                       warning("in tree %s: entry %s has bad mode %.6o\n",
+                            sha1_to_hex(item->object.sha1), entry.path, entry.mode);
+                       obj = lookup_unknown_object(entry.sha1);
+               }
                refs->ref[i++] = obj;
        }
        set_object_refs(&item->object, refs);
index 10816e95a07c23dda7554c102c306d7e9beca606..963bb89b08e64188ba278587f63f380653b019c6 100644 (file)
@@ -122,4 +122,12 @@ int read_mmfile(mmfile_t *ptr, const char *filename)
        return 0;
 }
 
+#define FIRST_FEW_BYTES 8000
+int buffer_is_binary(const char *ptr, unsigned long size)
+{
+       if (FIRST_FEW_BYTES < size)
+               size = FIRST_FEW_BYTES;
+       return !!memchr(ptr, 0, size);
+}
+
 
index 1918808081c00daf20a16a1592c0ed9be6d79be4..536f4e4d9784e0e5ffdc108a5be4bf758d1578ba 100644 (file)
@@ -18,5 +18,6 @@ int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);
 int read_mmfile(mmfile_t *ptr, const char *filename);
+int buffer_is_binary(const char *ptr, unsigned long size);
 
 #endif