diff options
-rw-r--r-- | Documentation/config.txt | 6 | ||||
-rw-r--r-- | Documentation/git-bisect.txt | 7 | ||||
-rw-r--r-- | Documentation/git-unpack-objects.txt | 3 | ||||
-rw-r--r-- | Documentation/pretty-formats.txt | 1 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | builtin-commit.c | 4 | ||||
-rw-r--r-- | builtin-unpack-objects.c | 177 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | config.c | 10 | ||||
-rw-r--r-- | diffcore-rename.c | 80 | ||||
-rwxr-xr-x | git-add--interactive.perl | 49 | ||||
-rwxr-xr-x | git-bisect.sh | 12 | ||||
-rwxr-xr-x | git-rebase.sh | 52 | ||||
-rwxr-xr-x | git-send-email.perl | 20 | ||||
-rw-r--r-- | gitweb/INSTALL | 6 | ||||
-rw-r--r-- | gitweb/README | 9 | ||||
-rwxr-xr-x | gitweb/gitweb.perl | 7 | ||||
-rw-r--r-- | log-tree.c | 6 | ||||
-rw-r--r-- | perl/Git.pm | 45 | ||||
-rw-r--r-- | pretty.c | 11 | ||||
-rw-r--r-- | receive-pack.c | 36 | ||||
-rwxr-xr-x | t/t3701-add-interactive.sh | 19 | ||||
-rwxr-xr-x | t/t5300-pack-object.sh | 95 | ||||
-rw-r--r-- | var.c | 3 |
24 files changed, 538 insertions, 123 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt index 04c01c5fdc..fe43b12572 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -991,6 +991,12 @@ imap:: The configuration variables in the 'imap' section are described in linkgit:git-imap-send[1]. +receive.fsckObjects:: + If it is set to true, git-receive-pack will check all received + objects. It will abort in the case of a malformed object or a + broken link. The result of an abort are only dangling objects. + Defaults to false. + receive.unpackLimit:: If the number of objects received in a push is below this limit then the objects will be unpacked into loose object diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 96585ae8d9..698ffde7ce 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -15,6 +15,7 @@ DESCRIPTION The command takes various subcommands, and different options depending on the subcommand: + git bisect help git bisect start [<bad> [<good>...]] [--] [<paths>...] git bisect bad [<rev>] git bisect good [<rev>...] @@ -29,6 +30,12 @@ This command uses 'git-rev-list --bisect' option to help drive the binary search process to find which change introduced a bug, given an old "good" commit object name and a later "bad" commit object name. +Getting help +~~~~~~~~~~~~ + +Use "git bisect" to get a short usage description, and "git bisect +help" or "git bisect -h" to get a long usage description. + Basic bisect commands: start, bad, good ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt index b79be3fd4c..3697896a06 100644 --- a/Documentation/git-unpack-objects.txt +++ b/Documentation/git-unpack-objects.txt @@ -40,6 +40,9 @@ OPTIONS and make the best effort to recover as many objects as possible. +--strict:: + Don't write objects with broken content or links. + Author ------ diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 0193c3ce58..e8bea3e18e 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -123,3 +123,4 @@ The placeholders are: - '%Creset': reset color - '%m': left, right or boundary mark - '%n': newline +- '%x00': print a byte from a hex code @@ -189,6 +189,7 @@ ETC_GITCONFIG = $(sysconfdir)/gitconfig # default configuration for gitweb GITWEB_CONFIG = gitweb_config.perl +GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf GITWEB_HOME_LINK_STR = projects GITWEB_SITENAME = GITWEB_PROJECTROOT = /pub/git @@ -1034,6 +1035,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \ -e 's|++GIT_BINDIR++|$(bindir)|g' \ -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \ + -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \ -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \ diff --git a/builtin-commit.c b/builtin-commit.c index 05dfc6973a..bcb7aaaa93 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -745,10 +745,8 @@ static int parse_and_validate_options(int argc, const char *argv[], die("No paths with --include/--only does not make sense."); if (argc == 0 && only && amend) only_include_assumed = "Clever... amending the last one with dirty index."; - if (argc > 0 && !also && !only) { + if (argc > 0 && !also && !only) only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; - also = 0; - } if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 50e07faa12..fecf0be779 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -7,11 +7,13 @@ #include "commit.h" #include "tag.h" #include "tree.h" +#include "tree-walk.h" #include "progress.h" #include "decorate.h" +#include "fsck.h" -static int dry_run, quiet, recover, has_errors; -static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file"; +static int dry_run, quiet, recover, has_errors, strict; +static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file"; /* We always read in 4kB chunks. */ static unsigned char buffer[4096]; @@ -19,6 +21,11 @@ static unsigned int offset, len; static off_t consumed_bytes; static SHA_CTX ctx; +/* + * When running under --strict mode, objects whose reachability are + * suspect are kept in core without getting written in the object + * store. + */ struct obj_buffer { char *buffer; unsigned long size; @@ -31,6 +38,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base) return lookup_decoration(&obj_decorate, base); } +static void add_object_buffer(struct object *object, char *buffer, unsigned long size) +{ + struct obj_buffer *obj; + obj = xcalloc(1, sizeof(struct obj_buffer)); + obj->buffer = buffer; + obj->size = size; + if (add_decoration(&obj_decorate, object, obj)) + die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1)); +} + /* * Make sure at least "min" bytes are available in the buffer, and * return the pointer to the buffer. @@ -134,19 +151,110 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1, struct obj_info { off_t offset; unsigned char sha1[20]; + struct object *obj; }; +#define FLAG_OPEN (1u<<20) +#define FLAG_WRITTEN (1u<<21) + static struct obj_info *obj_list; +unsigned nr_objects; + +/* + * Called only from check_object() after it verified this object + * is Ok. + */ +static void write_cached_object(struct object *obj) +{ + unsigned char sha1[20]; + struct obj_buffer *obj_buf = lookup_object_buffer(obj); + if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0) + die("failed to write object %s", sha1_to_hex(obj->sha1)); + obj->flags |= FLAG_WRITTEN; +} + +/* + * At the very end of the processing, write_rest() scans the objects + * that have reachability requirements and calls this function. + * Verify its reachability and validity recursively and write it out. + */ +static int check_object(struct object *obj, int type, void *data) +{ + if (!obj) + return 0; + + if (obj->flags & FLAG_WRITTEN) + return 1; + + if (type != OBJ_ANY && obj->type != type) + die("object type mismatch"); + + if (!(obj->flags & FLAG_OPEN)) { + unsigned long size; + int type = sha1_object_info(obj->sha1, &size); + if (type != obj->type || type <= 0) + die("object of unexpected type"); + obj->flags |= FLAG_WRITTEN; + return 1; + } + + if (fsck_object(obj, 1, fsck_error_function)) + die("Error in object"); + if (!fsck_walk(obj, check_object, 0)) + die("Error on reachable objects of %s", sha1_to_hex(obj->sha1)); + write_cached_object(obj); + return 1; +} + +static void write_rest(void) +{ + unsigned i; + for (i = 0; i < nr_objects; i++) + check_object(obj_list[i].obj, OBJ_ANY, 0); +} static void added_object(unsigned nr, enum object_type type, void *data, unsigned long size); +/* + * Write out nr-th object from the list, now we know the contents + * of it. Under --strict, this buffers structured objects in-core, + * to be checked at the end. + */ static void write_object(unsigned nr, enum object_type type, void *buf, unsigned long size) { - if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) - die("failed to write object"); - added_object(nr, type, buf, size); + if (!strict) { + if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + die("failed to write object"); + added_object(nr, type, buf, size); + free(buf); + obj_list[nr].obj = NULL; + } else if (type == OBJ_BLOB) { + struct blob *blob; + if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0) + die("failed to write object"); + added_object(nr, type, buf, size); + free(buf); + + blob = lookup_blob(obj_list[nr].sha1); + if (blob) + blob->object.flags |= FLAG_WRITTEN; + else + die("invalid blob object"); + obj_list[nr].obj = NULL; + } else { + struct object *obj; + int eaten; + hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1); + added_object(nr, type, buf, size); + obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten); + if (!obj) + die("invalid %s", typename(type)); + add_object_buffer(obj, buf, size); + obj->flags |= FLAG_OPEN; + obj_list[nr].obj = obj; + } } static void resolve_delta(unsigned nr, enum object_type type, @@ -163,9 +271,12 @@ static void resolve_delta(unsigned nr, enum object_type type, die("failed to apply delta"); free(delta); write_object(nr, type, result, result_size); - free(result); } +/* + * We now know the contents of an object (which is nr-th in the pack); + * resolve all the deltified objects that are based on it. + */ static void added_object(unsigned nr, enum object_type type, void *data, unsigned long size) { @@ -193,7 +304,24 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size, if (!dry_run && buf) write_object(nr, type, buf, size); - free(buf); + else + free(buf); +} + +static int resolve_against_held(unsigned nr, const unsigned char *base, + void *delta_data, unsigned long delta_size) +{ + struct object *obj; + struct obj_buffer *obj_buffer; + obj = lookup_object(base); + if (!obj) + return 0; + obj_buffer = lookup_object_buffer(obj); + if (!obj_buffer) + return 0; + resolve_delta(nr, obj->type, obj_buffer->buffer, + obj_buffer->size, delta_data, delta_size); + return 1; } static void unpack_delta_entry(enum object_type type, unsigned long delta_size, @@ -202,7 +330,6 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, void *delta_data, *base; unsigned long base_size; unsigned char base_sha1[20]; - struct object *obj; if (type == OBJ_REF_DELTA) { hashcpy(base_sha1, fill(20)); @@ -212,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, free(delta_data); return; } - if (!has_sha1_file(base_sha1)) { + if (has_sha1_file(base_sha1)) + ; /* Ok we have this one */ + else if (resolve_against_held(nr, base_sha1, + delta_data, delta_size)) + return; /* we are done */ + else { + /* cannot resolve yet --- queue it */ hashcpy(obj_list[nr].sha1, null_sha1); add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size); return; @@ -258,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, } } if (!base_found) { - /* The delta base object is itself a delta that - has not been resolved yet. */ + /* + * The delta base object is itself a delta that + * has not been resolved yet. + */ hashcpy(obj_list[nr].sha1, null_sha1); add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size); return; } } - obj = lookup_object(base_sha1); - if (obj) { - struct obj_buffer *obj_buf = lookup_object_buffer(obj); - if (obj_buf) { - resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size); - return; - } - } + if (resolve_against_held(nr, base_sha1, delta_data, delta_size)) + return; base = read_sha1_file(base_sha1, &type, &base_size); if (!base) { @@ -336,7 +465,8 @@ static void unpack_all(void) int i; struct progress *progress = NULL; struct pack_header *hdr = fill(sizeof(struct pack_header)); - unsigned nr_objects = ntohl(hdr->hdr_entries); + + nr_objects = ntohl(hdr->hdr_entries); if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) die("bad pack file"); @@ -347,6 +477,7 @@ static void unpack_all(void) if (!quiet) progress = start_progress("Unpacking objects", nr_objects); obj_list = xmalloc(nr_objects * sizeof(*obj_list)); + memset(obj_list, 0, nr_objects * sizeof(*obj_list)); for (i = 0; i < nr_objects; i++) { unpack_one(i); display_progress(progress, i + 1); @@ -382,6 +513,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) recover = 1; continue; } + if (!strcmp(arg, "--strict")) { + strict = 1; + continue; + } if (!prefixcmp(arg, "--pack_header=")) { struct pack_header *hdr; char *c; @@ -407,6 +542,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) unpack_all(); SHA1_Update(&ctx, buffer, offset); SHA1_Final(sha1, &ctx); + if (strict) + write_rest(); if (hashcmp(fill(20), sha1)) die("final sha1 did not match"); use(20); @@ -692,6 +692,7 @@ extern int git_parse_long(const char *, long *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); +extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); extern int git_config_string(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); @@ -303,8 +303,9 @@ unsigned long git_config_ulong(const char *name, const char *value) return ret; } -int git_config_bool(const char *name, const char *value) +int git_config_bool_or_int(const char *name, const char *value, int *is_bool) { + *is_bool = 1; if (!value) return 1; if (!*value) @@ -313,9 +314,16 @@ int git_config_bool(const char *name, const char *value) return 1; if (!strcasecmp(value, "false") || !strcasecmp(value, "no")) return 0; + *is_bool = 0; return git_config_int(name, value) != 0; } +int git_config_bool(const char *name, const char *value) +{ + int discard; + return git_config_bool_or_int(name, value, &discard); +} + int git_config_string(const char **dest, const char *var, const char *value) { if (!value) diff --git a/diffcore-rename.c b/diffcore-rename.c index 31941bcbbf..1369a5ec45 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -112,8 +112,8 @@ static int basename_same(struct diff_filespec *src, struct diff_filespec *dst) struct diff_score { int src; /* index in rename_src */ int dst; /* index in rename_dst */ - int score; - int name_score; + unsigned short score; + short name_score; }; static int estimate_similarity(struct diff_filespec *src, @@ -223,6 +223,12 @@ static int score_compare(const void *a_, const void *b_) { const struct diff_score *a = a_, *b = b_; + /* sink the unused ones to the bottom */ + if (a->dst < 0) + return (0 <= b->dst); + else if (b->dst < 0) + return -1; + if (a->score == b->score) return b->name_score - a->name_score; @@ -387,6 +393,22 @@ static int find_exact_renames(void) return i; } +#define NUM_CANDIDATE_PER_DST 4 +static void record_if_better(struct diff_score m[], struct diff_score *o) +{ + int i, worst; + + /* find the worst one */ + worst = 0; + for (i = 1; i < NUM_CANDIDATE_PER_DST; i++) + if (score_compare(&m[i], &m[worst]) > 0) + worst = i; + + /* is it better than the worst one? */ + if (score_compare(&m[worst], o) > 0) + m[worst] = *o; +} + void diffcore_rename(struct diff_options *options) { int detect_rename = options->detect_rename; @@ -474,47 +496,61 @@ void diffcore_rename(struct diff_options *options) goto cleanup; } - mx = xmalloc(sizeof(*mx) * num_create * num_src); + mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx)); for (dst_cnt = i = 0; i < rename_dst_nr; i++) { - int base = dst_cnt * num_src; struct diff_filespec *two = rename_dst[i].two; + struct diff_score *m; + if (rename_dst[i].pair) continue; /* dealt with exact match already. */ + + m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST]; + for (j = 0; j < NUM_CANDIDATE_PER_DST; j++) + m[j].dst = -1; + for (j = 0; j < rename_src_nr; j++) { struct diff_filespec *one = rename_src[j].one; - struct diff_score *m = &mx[base+j]; - m->src = j; - m->dst = i; - m->score = estimate_similarity(one, two, - minimum_score); - m->name_score = basename_same(one, two); + struct diff_score this_src; + this_src.score = estimate_similarity(one, two, + minimum_score); + this_src.name_score = basename_same(one, two); + this_src.dst = i; + this_src.src = j; + record_if_better(m, &this_src); diff_free_filespec_blob(one); } /* We do not need the text anymore */ diff_free_filespec_blob(two); dst_cnt++; } + /* cost matrix sorted by most to least similar pair */ - qsort(mx, num_create * num_src, sizeof(*mx), score_compare); - for (i = 0; i < num_create * num_src; i++) { - struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; - struct diff_filespec *src; + qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare); + + for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) { + struct diff_rename_dst *dst; + + if ((mx[i].dst < 0) || + (mx[i].score < minimum_score)) + break; /* there is no more usable pair. */ + dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (mx[i].score < minimum_score) - break; /* there is no more usable pair. */ - src = rename_src[mx[i].src].one; - if (src->rename_used) + if (rename_src[mx[i].src].one->rename_used) continue; record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); rename_count++; } - for (i = 0; i < num_create * num_src; i++) { - struct diff_rename_dst *dst = &rename_dst[mx[i].dst]; + + for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) { + struct diff_rename_dst *dst; + + if ((mx[i].dst < 0) || + (mx[i].score < minimum_score)) + break; /* there is no more usable pair. */ + dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (mx[i].score < minimum_score) - break; /* there is no more usable pair. */ record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); rename_count++; } diff --git a/git-add--interactive.perl b/git-add--interactive.perl index a0a81f134a..903953e68e 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -550,6 +550,21 @@ sub parse_diff { return @hunk; } +sub parse_diff_header { + my $src = shift; + + my $head = { TEXT => [], DISPLAY => [] }; + my $mode = { TEXT => [], DISPLAY => [] }; + + for (my $i = 0; $i < @{$src->{TEXT}}; $i++) { + my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? + $mode : $head; + push @{$dest->{TEXT}}, $src->{TEXT}->[$i]; + push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i]; + } + return ($head, $mode); +} + sub hunk_splittable { my ($text) = @_; @@ -795,9 +810,40 @@ sub patch_update_file { my ($ix, $num); my $path = shift; my ($head, @hunk) = parse_diff($path); + ($head, my $mode) = parse_diff_header($head); for (@{$head->{DISPLAY}}) { print; } + + if (@{$mode->{TEXT}}) { + while (1) { + print @{$mode->{DISPLAY}}; + print colored $prompt_color, + "Stage mode change [y/n/a/d/?]? "; + my $line = <STDIN>; + if ($line =~ /^y/i) { + $mode->{USE} = 1; + last; + } + elsif ($line =~ /^n/i) { + $mode->{USE} = 0; + last; + } + elsif ($line =~ /^a/i) { + $_->{USE} = 1 foreach ($mode, @hunk); + last; + } + elsif ($line =~ /^d/i) { + $_->{USE} = 0 foreach ($mode, @hunk); + last; + } + else { + help_patch_cmd(''); + next; + } + } + } + $num = scalar @hunk; $ix = 0; @@ -920,6 +966,9 @@ sub patch_update_file { my $n_lofs = 0; my @result = (); + if ($mode->{USE}) { + push @result, @{$mode->{TEXT}}; + } for (@hunk) { my $text = $_->{TEXT}; my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = diff --git a/git-bisect.sh b/git-bisect.sh index 5177a2a114..408775a51e 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,7 +1,9 @@ #!/bin/sh -USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]' -LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...] +USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]' +LONG_USAGE='git bisect help + print this long help message. +git bisect start [<bad> [<good>...]] [--] [<pathspec>...] reset bisect state and start bisection. git bisect bad [<rev>] mark <rev> a known-bad revision. @@ -20,7 +22,9 @@ git bisect replay <logfile> git bisect log show bisect log. git bisect run <cmd>... - use <cmd>... to automatically bisect.' + use <cmd>... to automatically bisect. + +Please use "git help bisect" to get the full man page.' OPTIONS_SPEC= . git-sh-setup @@ -461,6 +465,8 @@ case "$#" in cmd="$1" shift case "$cmd" in + help) + git bisect -h ;; start) bisect_start "$@" ;; bad|good|skip) diff --git a/git-rebase.sh b/git-rebase.sh index 60c458f201..9b13b833cb 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -309,22 +309,42 @@ then } fi -# If the branch to rebase is given, first switch to it. +# If the branch to rebase is given, that is the branch we will rebase +# $branch_name -- branch being rebased, or HEAD (already detached) +# $orig_head -- commit object name of tip of the branch before rebasing +# $head_name -- refs/heads/<that-branch> or "detached HEAD" +switch_to= case "$#" in 2) + # Is it "rebase other $branchname" or "rebase other $commit"? branch_name="$2" - git-checkout "$2" || usage + switch_to="$2" + + if git show-ref --verify --quiet -- "refs/heads/$2" && + branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null) + then + head_name="refs/heads/$2" + elif branch=$(git rev-parse --verify "$2" 2>/dev/null) + then + head_name="detached HEAD" + else + usage + fi ;; *) + # Do not need to switch branches, we are already on it. if branch_name=`git symbolic-ref -q HEAD` then + head_name=$branch_name branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'` else + head_name="detached HEAD" branch_name=HEAD ;# detached fi + branch=$(git rev-parse --verify "${branch_name}^0") || exit ;; esac -branch=$(git rev-parse --verify "${branch_name}^0") || exit +orig_head=$branch # Now we are rebasing commits $upstream..$branch on top of $onto @@ -335,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || git checkout "$switch_to" echo >&2 "Current branch $branch_name is up to date." exit 0 fi @@ -346,22 +368,11 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi -# move to a detached HEAD -orig_head=$(git rev-parse HEAD^0) -head_name=$(git symbolic-ref HEAD 2> /dev/null) -case "$head_name" in -'') - head_name="detached HEAD" - ;; -*) - git checkout "$orig_head" > /dev/null 2>&1 || - die "could not detach HEAD" - ;; -esac - -# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. +# Detach HEAD and reset the tree echo "First, rewinding head to replay your work on top of it..." -git-reset --hard "$onto" +git checkout "$onto^0" >/dev/null 2>&1 || + die "could not detach HEAD" +# git reset --hard "$onto^0" # If the $onto is a proper descendant of the tip of the branch, then # we just fast forwarded. @@ -374,7 +385,8 @@ fi if test -z "$do_merge" then - git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | + git format-patch -k --stdout --full-index --ignore-if-in-upstream \ + "$upstream..$orig_head" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -397,7 +409,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD` +for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/git-send-email.perl b/git-send-email.perl index be4a20d7cd..9e568bf9c0 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -168,7 +168,8 @@ my $envelope_sender; # Example reply to: #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; -my $repo = Git->repository(); +my $repo = eval { Git->repository() }; +my @repo = $repo ? ($repo) : (); my $term = eval { $ENV{"GIT_SEND_EMAIL_NOTTY"} ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT @@ -271,25 +272,25 @@ sub read_config { foreach my $setting (keys %config_bool_settings) { my $target = $config_bool_settings{$setting}->[0]; - $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target); + $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target); } foreach my $setting (keys %config_settings) { my $target = $config_settings{$setting}; if (ref($target) eq "ARRAY") { unless (@$target) { - my @values = $repo->config("$prefix.$setting"); + my @values = Git::config(@repo, "$prefix.$setting"); @$target = @values if (@values && defined $values[0]); } } else { - $$target = $repo->config("$prefix.$setting") unless (defined $$target); + $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target); } } } # read configuration from [sendemail "$identity"], fall back on [sendemail] -$identity = $repo->config("sendemail.identity") unless (defined $identity); +$identity = Git::config(@repo, "sendemail.identity") unless (defined $identity); read_config("sendemail.$identity") if (defined $identity); read_config("sendemail"); @@ -327,8 +328,9 @@ if (0) { } } -my ($repoauthor) = $repo->ident_person('author'); -my ($repocommitter) = $repo->ident_person('committer'); +my ($repoauthor, $repocommitter); +($repoauthor) = Git::ident_person(@repo, 'author'); +($repocommitter) = Git::ident_person(@repo, 'committer'); # Verify the user input @@ -415,7 +417,7 @@ if (@files) { my $prompting = 0; if (!defined $sender) { - $sender = $repoauthor || $repocommitter; + $sender = $repoauthor || $repocommitter || ''; while (1) { $_ = $term->readline("Who should the emails appear to be from? [$sender] "); @@ -509,7 +511,7 @@ GIT: for the patch you are writing. EOT close(C); - my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; + my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; system('sh', '-c', '$0 $@', $editor, $compose_filename); open(C2,">",$compose_filename . ".final") diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 9cd5b0a2b1..743f2d4442 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -95,7 +95,11 @@ for gitweb (in gitweb/README). by default it is file named gitweb_config.perl in the same place as gitweb.cgi script. You can control default place for config file using GITWEB_CONFIG build configuration variable, and you can set it - using GITWEB_CONFIG environmental variable. + using GITWEB_CONFIG environmental variable. If this file does not + exist, gitweb looks for a system-wide configuration file, normally + /etc/gitweb.conf. You can change the default using the + GITWEB_CONFIG_SYSTEM build configuration variable, and override it + through GITWEB_CONFIG_SYSTEM environmental variable. - Gitweb config file is [fragment] of perl code. You can set variables using "our $variable = value"; text from "#" character until the end diff --git a/gitweb/README b/gitweb/README index 2163071047..8dfe335f73 100644 --- a/gitweb/README +++ b/gitweb/README @@ -100,13 +100,20 @@ You can specify the following configuration variables when building GIT: is set when gitweb.cgi is executed, then the file specified in the environment variable will be loaded instead of the file specified when gitweb.cgi was created. [Default: gitweb_config.perl] + * GITWEB_CONFIG_SYSTEM + This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG + does not exist. If the environment variable GITWEB_CONFIG_SYSTEM is set + when gitweb.cgi is executed, then the file specified in the environment + variable will be loaded instead of the file specified when gitweb.cgi was + created. [Default: /etc/gitweb.conf] Runtime gitweb configuration ---------------------------- You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG` -(defaults to 'gitweb_config.perl' in the same directory as the CGI). +(defaults to 'gitweb_config.perl' in the same directory as the CGI), and +as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf). The most notable thing that is not configurable at compile time are the optional features, stored in the '%features' variable. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 73d098a433..e69d7fd07b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -369,7 +369,12 @@ sub filter_snapshot_fmts { } our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; -do $GITWEB_CONFIG if -e $GITWEB_CONFIG; +if (-e $GITWEB_CONFIG) { + do $GITWEB_CONFIG; +} else { + our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++"; + do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM; +} # version of the core git binary our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown"; diff --git a/log-tree.c b/log-tree.c index 5b2963998c..9d54061601 100644 --- a/log-tree.c +++ b/log-tree.c @@ -317,8 +317,10 @@ void show_log(struct rev_info *opt, const char *sep) if (opt->show_log_size) printf("log size %i\n", (int)msgbuf.len); - if (msgbuf.len) - printf("%s%s%s", msgbuf.buf, extra, sep); + if (msgbuf.len) { + fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout); + printf("%s%s", extra, sep); + } strbuf_release(&msgbuf); } diff --git a/perl/Git.pm b/perl/Git.pm index a2812ea612..2e7f896bae 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -487,22 +487,20 @@ does. In scalar context requires the variable to be set only one time (exception is thrown otherwise), in array context returns allows the variable to be set multiple times and returns all the values. -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { + my @cmd = ('config'); + unshift @cmd, $self if $self; if (wantarray) { - return $self->command('config', '--get-all', $var); + return command(@cmd, '--get-all', $var); } else { - return $self->command_oneline('config', '--get', $var); + return command_oneline(@cmd, '--get', $var); } } catch Git::Error::Command with { my $E = shift; @@ -522,20 +520,17 @@ Retrieve the bool configuration C<VARIABLE>. The return value is usable as a boolean in perl (and C<undef> if it's not defined, of course). -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config_bool { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { - my $val = $self->command_oneline('config', '--bool', '--get', - $var); + my @cmd = ('config', '--bool', '--get', $var); + unshift @cmd, $self if $self; + my $val = command_oneline(@cmd); return undef unless defined $val; return $val eq 'true'; } catch Git::Error::Command with { @@ -557,19 +552,17 @@ or 'g' in the config file will cause the value to be multiplied by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output. It would return C<undef> if configuration variable is not defined, -Must be called on a repository instance. - This currently wraps command('config') so it is not so fast. =cut sub config_int { - my ($self, $var) = @_; - $self->repo_path() - or throw Error::Simple("not a repository"); + my ($self, $var) = _maybe_self(@_); try { - return $self->command_oneline('config', '--int', '--get', $var); + my @cmd = ('config', '--int', '--get', $var); + unshift @cmd, $self if $self; + return command_oneline(@cmd); } catch Git::Error::Command with { my $E = shift; if ($E->value() == 1) { @@ -639,15 +632,15 @@ The synopsis is like: "$name <$email>" eq ident_person($name); $time_tz =~ /^\d+ [+-]\d{4}$/; -Both methods must be called on a repository instance. - =cut sub ident { - my ($self, $type) = @_; + my ($self, $type) = _maybe_self(@_); my $identstr; if (lc $type eq lc 'committer' or lc $type eq lc 'author') { - $identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT'); + my @cmd = ('var', 'GIT_'.uc($type).'_IDENT'); + unshift @cmd, $self if $self; + $identstr = command_oneline(@cmd); } else { $identstr = $type; } @@ -659,8 +652,8 @@ sub ident { } sub ident_person { - my ($self, @ident) = @_; - $#ident == 0 and @ident = $self->ident($ident[0]); + my ($self, @ident) = _maybe_self(@_); + $#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]); return "$ident[0] <$ident[1]>"; } @@ -457,6 +457,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, const struct commit *commit = c->commit; const char *msg = commit->buffer; struct commit_list *p; + int h1, h2; /* these are independent of the commit */ switch (placeholder[0]) { @@ -478,6 +479,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 'n': /* newline */ strbuf_addch(sb, '\n'); return 1; + case 'x': + /* %x00 == NUL, %x0a == LF, etc. */ + if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) && + h1 <= 16 && + 0 <= (h2 = hexval_table[0xff & placeholder[2]]) && + h2 <= 16) { + strbuf_addch(sb, (h1<<4)|h2); + return 3; + } else + return 0; } /* these depend on the commit */ diff --git a/receive-pack.c b/receive-pack.c index f83ae87e15..828d49001d 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -10,6 +10,7 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>"; static int deny_non_fast_forwards = 0; +static int receive_fsck_objects; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int unpack_limit = 100; @@ -35,6 +36,11 @@ static int receive_pack_config(const char *var, const char *value) return 0; } + if (strcmp(var, "receive.fsckobjects") == 0) { + receive_fsck_objects = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value); } @@ -368,11 +374,13 @@ static const char *unpack(void) ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); if (ntohl(hdr.hdr_entries) < unpack_limit) { - int code; - const char *unpacker[3]; - unpacker[0] = "unpack-objects"; - unpacker[1] = hdr_arg; - unpacker[2] = NULL; + int code, i = 0; + const char *unpacker[4]; + unpacker[i++] = "unpack-objects"; + if (receive_fsck_objects) + unpacker[i++] = "--strict"; + unpacker[i++] = hdr_arg; + unpacker[i++] = NULL; code = run_command_v_opt(unpacker, RUN_GIT_CMD); switch (code) { case 0: @@ -393,8 +401,8 @@ static const char *unpack(void) return "unpacker exited with error code"; } } else { - const char *keeper[6]; - int s, status; + const char *keeper[7]; + int s, status, i = 0; char keep_arg[256]; struct child_process ip; @@ -402,12 +410,14 @@ static const char *unpack(void) if (gethostname(keep_arg + s, sizeof(keep_arg) - s)) strcpy(keep_arg + s, "localhost"); - keeper[0] = "index-pack"; - keeper[1] = "--stdin"; - keeper[2] = "--fix-thin"; - keeper[3] = hdr_arg; - keeper[4] = keep_arg; - keeper[5] = NULL; + keeper[i++] = "index-pack"; + keeper[i++] = "--stdin"; + if (receive_fsck_objects) + keeper[i++] = "--strict"; + keeper[i++] = "--fix-thin"; + keeper[i++] = hdr_arg; + keeper[i++] = keep_arg; + keeper[i++] = NULL; memset(&ip, 0, sizeof(ip)); ip.argv = keeper; ip.out = -1; diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 77c90f6fa0..f15be93e77 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -66,4 +66,23 @@ test_expect_success 'revert works (commit)' ' grep "unchanged *+3/-0 file" output ' +test_expect_success 'patch does not affect mode' ' + git reset --hard && + echo content >>file && + chmod +x file && + printf "n\\ny\\n" | git add -p && + git show :file | grep content && + git diff file | grep "new mode" +' + +test_expect_success 'stage mode but not hunk' ' + git reset --hard && + echo content >>file && + chmod +x file && + printf "y\\nn\\n" | git add -p && + git diff --cached file | grep "new mode" && + git diff file | grep "+content" +' + + test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index c955fe44f5..983a39398f 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -274,4 +274,99 @@ test_expect_success \ packname_4=$(git pack-objects test-4 <obj-list) && test 3 = $(ls test-4-*.pack | wc -l)' +test_expect_success 'unpacking with --strict' ' + + git config --unset pack.packsizelimit && + for j in a b c d e f g + do + for i in 0 1 2 3 4 5 6 7 8 9 + do + o=$(echo $j$i | git hash-object -w --stdin) && + echo "100644 $o 0 $j$i" + done + done >LIST && + rm -f .git/index && + git update-index --index-info <LIST && + LIST=$(git write-tree) && + rm -f .git/index && + head -n 10 LIST | git update-index --index-info && + LI=$(git write-tree) && + rm -f .git/index && + tail -n 10 LIST | git update-index --index-info && + ST=$(git write-tree) && + PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \ + git pack-objects test-5 ) && + PACK6=$( ( + echo "$LIST" + echo "$LI" + echo "$ST" + ) | git pack-objects test-6 ) && + test_create_repo test-5 && + ( + cd test-5 && + git unpack-objects --strict <../test-5-$PACK5.pack && + git ls-tree -r $LIST && + git ls-tree -r $LI && + git ls-tree -r $ST + ) && + test_create_repo test-6 && + ( + # tree-only into empty repo -- many unreachables + cd test-6 && + test_must_fail git unpack-objects --strict <../test-6-$PACK6.pack + ) && + ( + # already populated -- no unreachables + cd test-5 && + git unpack-objects --strict <../test-6-$PACK6.pack + ) +' + +test_expect_success 'index-pack with --strict' ' + + for j in a b c d e f g + do + for i in 0 1 2 3 4 5 6 7 8 9 + do + o=$(echo $j$i | git hash-object -w --stdin) && + echo "100644 $o 0 $j$i" + done + done >LIST && + rm -f .git/index && + git update-index --index-info <LIST && + LIST=$(git write-tree) && + rm -f .git/index && + head -n 10 LIST | git update-index --index-info && + LI=$(git write-tree) && + rm -f .git/index && + tail -n 10 LIST | git update-index --index-info && + ST=$(git write-tree) && + PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \ + git pack-objects test-5 ) && + PACK6=$( ( + echo "$LIST" + echo "$LI" + echo "$ST" + ) | git pack-objects test-6 ) && + test_create_repo test-7 && + ( + cd test-7 && + git index-pack --strict --stdin <../test-5-$PACK5.pack && + git ls-tree -r $LIST && + git ls-tree -r $LI && + git ls-tree -r $ST + ) && + test_create_repo test-8 && + ( + # tree-only into empty repo -- many unreachables + cd test-8 && + test_must_fail git index-pack --strict --stdin <../test-6-$PACK6.pack + ) && + ( + # already populated -- no unreachables + cd test-7 && + git index-pack --strict --stdin <../test-6-$PACK6.pack + ) +' + test_done @@ -51,11 +51,12 @@ static int show_config(const char *var, const char *value) int main(int argc, char **argv) { const char *val; + int nongit; if (argc != 2) { usage(var_usage); } - setup_git_directory(); + setup_git_directory_gently(&nongit); val = NULL; if (strcmp(argv[1], "-l") == 0) { |