diff options
44 files changed, 983 insertions, 545 deletions
diff --git a/.gitignore b/.gitignore index 15aed70631..27e5aeb8a0 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Documentation/config.txt b/Documentation/config.txt index 5868d587a9..4d0bd37af9 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -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 diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index a33d157b97..363edb0fcf 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -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. diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index 8c68cf0372..ed6413a3c7 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -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 diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 7757abe621..6e4d15829d 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -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 diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 4e3e02756c..aee2c1bdc7 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -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) diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt index ff6184b0f7..b1b3ec9772 100644 --- a/Documentation/git-unpack-objects.txt +++ b/Documentation/git-unpack-objects.txt @@ -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 diff --git a/Documentation/git.txt b/Documentation/git.txt index 98860af045..4b567d8028 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -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':: @@ -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 diff --git a/builtin-branch.c b/builtin-branch.c index a5b6bbef6e..67f46c1ae2 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -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/")) { diff --git a/builtin-fsck.c b/builtin-fsck.c index 9959818ced..bacae5dfa6 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -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); } diff --git a/builtin-log.c b/builtin-log.c index 37447123f9..212cdfc769 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -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); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index f7c066b24b..48a313516d 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -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); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index ccb25f6a9c..8b9740c277 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -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; } diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 37addb25fa..497903a85a 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -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; @@ -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); @@ -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); @@ -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; @@ -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, diff --git a/git-cvsimport.perl b/git-cvsimport.perl index f68afe78a0..4e6c9c6cc7 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -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); } } diff --git a/git-mergetool.sh b/git-mergetool.sh index e62351bcba..bb21b037d6 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -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] ...' diff --git a/git-sh-setup.sh b/git-sh-setup.sh index f24c7f2d23..0de49e8459 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -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." } diff --git a/git-svn.perl b/git-svn.perl index e35006142a..886b898fcc 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -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 ", diff --git a/git-tag.sh b/git-tag.sh index 6f0b7a7219..37cee978d2 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -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 @@ -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"); diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c3921cb0ba..e92596c295 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -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/ / /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/ / /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"; @@ -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); @@ -196,9 +196,9 @@ const char *fmt_ident(const char *name, const char *email, 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; diff --git a/index-pack.c b/index-pack.c index 58c4a9c41d..82c8da3683 100644 --- a/index-pack.c +++ b/index-pack.c @@ -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, diff --git a/merge-recursive.c b/merge-recursive.c index 8f72b2c079..4a82b741ae 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -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, @@ -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; } diff --git a/pack-write.c b/pack-write.c index ae2e481e55..1cf5f7c9f0 100644 --- a/pack-write.c +++ b/pack-write.c @@ -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, @@ -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); @@ -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 index 0000000000..ec4996637d --- /dev/null +++ b/t/t1500-rev-parse.sh @@ -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 index 0000000000..aadeeab9ab --- /dev/null +++ b/t/t1501-worktree.sh @@ -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 diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 828d553a4b..6f6d8844e8 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -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 diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index e223c074f0..5500505d8b 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -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)' diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh index a398556137..058db9cc52 100755 --- a/t/t6024-recursive-merge.sh +++ b/t/t6024-recursive-merge.sh @@ -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 diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index b92ab63312..44ae503b99 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -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 diff --git a/t/test-lib.sh b/t/test-lib.sh index dee3ad7621..b61e1d598d 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -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 @@ -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); diff --git a/xdiff-interface.c b/xdiff-interface.c index 10816e95a0..963bb89b08 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -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); +} + diff --git a/xdiff-interface.h b/xdiff-interface.h index 1918808081..536f4e4d97 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -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 |