From eb32d236df0c16b936b04f0c5402addb61cdb311 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 21 Sep 2006 00:06:49 -0400 Subject: [PATCH 001/114] introduce delta objects with offset to base This adds a new object, namely OBJ_OFS_DELTA, renames OBJ_DELTA to OBJ_REF_DELTA to better make the distinction between those two delta objects, and adds support for the handling of those new delta objects in sha1_file.c only. The OBJ_OFS_DELTA contains a relative offset from the delta object's position in a pack instead of the 20-byte SHA1 reference to identify the base object. Since the base is likely to be not so far away, the relative offset is more likely to have a smaller encoding on average than an absolute offset. And for those delta objects the base must always be stored first because there is no way to know the distance of later objects when streaming a pack. Hence this relative offset is always meant to be negative. The offset encoding is slightly denser than the one used for object size -- credits to (whoever this is) for bringing it to my attention. This allows for pack size reduction between 3.2% (Linux-2.6) to over 5% (linux-historic). Runtime pack access should be faster too since delta replay does skip a search in the pack index for each delta in a chain. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 16 +++---- builtin-unpack-objects.c | 2 +- cache.h | 5 ++- index-pack.c | 8 ++-- sha1_file.c | 96 +++++++++++++++++++++++++++------------- 5 files changed, 82 insertions(+), 45 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 96c069a81d..c62734a2a6 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -232,7 +232,7 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha int n = 1; unsigned char c; - if (type < OBJ_COMMIT || type > OBJ_DELTA) + if (type < OBJ_COMMIT || type > OBJ_REF_DELTA) die("bad type %d", type); c = (type << 4) | (size & 15); @@ -297,7 +297,7 @@ static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data used = unpack_object_header_gently(data, len, &type, &size); if (!used) return -1; - if (type == OBJ_DELTA) + if (type == OBJ_REF_DELTA) used += 20; /* skip base object name */ data += used; len -= used; @@ -340,7 +340,7 @@ static unsigned long write_object(struct sha1file *f, obj_type = entry->type; if (! entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ - else if (obj_type == OBJ_DELTA) + else if (obj_type == OBJ_REF_DELTA) to_reuse = 1; /* check_object() decided it for us */ else if (obj_type != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ @@ -380,7 +380,7 @@ static unsigned long write_object(struct sha1file *f, if (entry->delta) { buf = delta_against(buf, size, entry); size = entry->delta_size; - obj_type = OBJ_DELTA; + obj_type = OBJ_REF_DELTA; } /* * The object header is a byte of 'type' followed by zero or @@ -409,11 +409,11 @@ static unsigned long write_object(struct sha1file *f, sha1write(f, buf, datalen); unuse_packed_git(p); hdrlen = 0; /* not really */ - if (obj_type == OBJ_DELTA) + if (obj_type == OBJ_REF_DELTA) reused_delta++; reused++; } - if (obj_type == OBJ_DELTA) + if (obj_type == OBJ_REF_DELTA) written_delta++; written++; return hdrlen + datalen; @@ -916,7 +916,7 @@ static void check_object(struct object_entry *entry) * delta. */ if (!no_reuse_delta && - entry->in_pack_type == OBJ_DELTA && + entry->in_pack_type == OBJ_REF_DELTA && (base_entry = locate_object_entry(base)) && (!base_entry->preferred_base)) { @@ -929,7 +929,7 @@ static void check_object(struct object_entry *entry) /* uncompressed size of the delta data */ entry->size = entry->delta_size = size; entry->delta = base_entry; - entry->type = OBJ_DELTA; + entry->type = OBJ_REF_DELTA; entry->delta_sibling = base_entry->delta_child; base_entry->delta_child = entry; diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index 4f96bcae32..c6c6368956 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -241,7 +241,7 @@ static void unpack_one(unsigned nr, unsigned total) case OBJ_TAG: unpack_non_delta_entry(type, size); return; - case OBJ_DELTA: + case OBJ_REF_DELTA: unpack_delta_entry(size); return; default: diff --git a/cache.h b/cache.h index 97debd03c5..3c5415e77d 100644 --- a/cache.h +++ b/cache.h @@ -274,8 +274,9 @@ enum object_type { OBJ_TREE = 2, OBJ_BLOB = 3, OBJ_TAG = 4, - /* 5/6 for future expansion */ - OBJ_DELTA = 7, + /* 5 for future expansion */ + OBJ_OFS_DELTA = 6, + OBJ_REF_DELTA = 7, OBJ_BAD, }; diff --git a/index-pack.c b/index-pack.c index 80bc6cb45b..aef7f0a32e 100644 --- a/index-pack.c +++ b/index-pack.c @@ -158,7 +158,7 @@ static void *unpack_raw_entry(unsigned long offset, } switch (type) { - case OBJ_DELTA: + case OBJ_REF_DELTA: if (pos + 20 >= pack_limit) bad_object(offset, "object extends past end of pack"); hashcpy(delta_base, pack_base + pos); @@ -301,7 +301,7 @@ static void parse_pack_objects(void) data = unpack_raw_entry(offset, &obj->type, &data_size, base_sha1, &offset); obj->real_type = obj->type; - if (obj->type == OBJ_DELTA) { + if (obj->type == OBJ_REF_DELTA) { struct delta_entry *delta = &deltas[nr_deltas++]; delta->obj = obj; hashcpy(delta->base_sha1, base_sha1); @@ -328,7 +328,7 @@ static void parse_pack_objects(void) struct object_entry *obj = &objects[i]; int j, first, last; - if (obj->type == OBJ_DELTA) + if (obj->type == OBJ_REF_DELTA) continue; if (find_deltas_based_on_sha1(obj->sha1, &first, &last)) continue; @@ -341,7 +341,7 @@ static void parse_pack_objects(void) /* Check for unresolved deltas */ for (i = 0; i < nr_deltas; i++) { - if (deltas[i].obj->real_type == OBJ_DELTA) + if (deltas[i].obj->real_type == OBJ_REF_DELTA) die("packfile '%s' has unresolved deltas", pack_name); } } diff --git a/sha1_file.c b/sha1_file.c index 27b1ebb720..fdb4588280 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -883,26 +883,61 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l return unpack_sha1_rest(&stream, hdr, *size); } +static unsigned long get_delta_base(struct packed_git *p, + unsigned long offset, + enum object_type kind, + unsigned long delta_obj_offset, + unsigned long *base_obj_offset) +{ + unsigned char *base_info = (unsigned char *) p->pack_base + offset; + unsigned long base_offset; + + /* there must be at least 20 bytes left regardless of delta type */ + if (p->pack_size <= offset + 20) + die("truncated pack file"); + + if (kind == OBJ_OFS_DELTA) { + unsigned used = 0; + unsigned char c = base_info[used++]; + base_offset = c & 127; + while (c & 128) { + base_offset += 1; + if (!base_offset || base_offset & ~(~0UL >> 7)) + die("offset value overflow for delta base object"); + c = base_info[used++]; + base_offset = (base_offset << 7) + (c & 127); + } + base_offset = delta_obj_offset - base_offset; + if (base_offset >= delta_obj_offset) + die("delta base offset out of bound"); + offset += used; + } else if (kind == OBJ_REF_DELTA) { + /* The base entry _must_ be in the same pack */ + base_offset = find_pack_entry_one(base_info, p); + if (!base_offset) + die("failed to find delta-pack base object %s", + sha1_to_hex(base_info)); + offset += 20; + } else + die("I am totally screwed"); + *base_obj_offset = base_offset; + return offset; +} + /* forward declaration for a mutually recursive function */ static int packed_object_info(struct packed_git *p, unsigned long offset, char *type, unsigned long *sizep); static int packed_delta_info(struct packed_git *p, unsigned long offset, + enum object_type kind, + unsigned long obj_offset, char *type, unsigned long *sizep) { unsigned long base_offset; - unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset; - if (p->pack_size < offset + 20) - die("truncated pack file"); - /* The base entry _must_ be in the same pack */ - base_offset = find_pack_entry_one(base_sha1, p); - if (!base_offset) - die("failed to find delta-pack base object %s", - sha1_to_hex(base_sha1)); - offset += 20; + offset = get_delta_base(p, offset, kind, obj_offset, &base_offset); /* We choose to only get the type of the base object and * ignore potentially corrupt pack file that expects the delta @@ -975,7 +1010,7 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, use_packed_git(p); ptr = offset; ptr = unpack_object_header(p, ptr, kindp, sizep); - if (*kindp != OBJ_DELTA) + if (*kindp != OBJ_REF_DELTA) goto done; hashcpy(base, (unsigned char *) p->pack_base + ptr); status = 0; @@ -992,11 +1027,12 @@ void packed_object_info_detail(struct packed_git *p, unsigned int *delta_chain_length, unsigned char *base_sha1) { - unsigned long val; + unsigned long obj_offset, val; unsigned char *next_sha1; enum object_type kind; *delta_chain_length = 0; + obj_offset = offset; offset = unpack_object_header(p, offset, &kind, size); for (;;) { @@ -1011,7 +1047,13 @@ void packed_object_info_detail(struct packed_git *p, strcpy(type, type_names[kind]); *store_size = 0; /* notyet */ return; - case OBJ_DELTA: + case OBJ_OFS_DELTA: + get_delta_base(p, offset, kind, obj_offset, &offset); + if (*delta_chain_length == 0) { + /* TODO: find base_sha1 as pointed by offset */ + } + break; + case OBJ_REF_DELTA: if (p->pack_size <= offset + 20) die("pack file %s records an incomplete delta base", p->pack_name); @@ -1021,6 +1063,7 @@ void packed_object_info_detail(struct packed_git *p, offset = find_pack_entry_one(next_sha1, p); break; } + obj_offset = offset; offset = unpack_object_header(p, offset, &kind, &val); (*delta_chain_length)++; } @@ -1029,15 +1072,15 @@ void packed_object_info_detail(struct packed_git *p, static int packed_object_info(struct packed_git *p, unsigned long offset, char *type, unsigned long *sizep) { - unsigned long size; + unsigned long size, obj_offset = offset; enum object_type kind; offset = unpack_object_header(p, offset, &kind, &size); - if (kind == OBJ_DELTA) - return packed_delta_info(p, offset, type, sizep); - switch (kind) { + case OBJ_OFS_DELTA: + case OBJ_REF_DELTA: + return packed_delta_info(p, offset, kind, obj_offset, type, sizep); case OBJ_COMMIT: case OBJ_TREE: case OBJ_BLOB: @@ -1083,23 +1126,15 @@ static void *unpack_compressed_entry(struct packed_git *p, static void *unpack_delta_entry(struct packed_git *p, unsigned long offset, unsigned long delta_size, + enum object_type kind, + unsigned long obj_offset, char *type, unsigned long *sizep) { void *delta_data, *result, *base; unsigned long result_size, base_size, base_offset; - unsigned char *base_sha1; - - if (p->pack_size < offset + 20) - die("truncated pack file"); - /* The base entry _must_ be in the same pack */ - base_sha1 = (unsigned char*)p->pack_base + offset; - base_offset = find_pack_entry_one(base_sha1, p); - if (!base_offset) - die("failed to find delta-pack base object %s", - sha1_to_hex(base_sha1)); - offset += 20; + offset = get_delta_base(p, offset, kind, obj_offset, &base_offset); base = unpack_entry_gently(p, base_offset, type, &base_size); if (!base) die("failed to read delta base object at %lu from %s", @@ -1136,13 +1171,14 @@ static void *unpack_entry(struct pack_entry *entry, void *unpack_entry_gently(struct packed_git *p, unsigned long offset, char *type, unsigned long *sizep) { - unsigned long size; + unsigned long size, obj_offset = offset; enum object_type kind; offset = unpack_object_header(p, offset, &kind, &size); switch (kind) { - case OBJ_DELTA: - return unpack_delta_entry(p, offset, size, type, sizep); + case OBJ_OFS_DELTA: + case OBJ_REF_DELTA: + return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep); case OBJ_COMMIT: case OBJ_TREE: case OBJ_BLOB: From 209c554ab45701fb78de77fdcec803fef2c8fd39 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 21 Sep 2006 00:07:39 -0400 Subject: [PATCH 002/114] teach git-unpack-objects about deltas with offset to base For delta resolution to be possible, a list of sha1/offset tuple must be constructed in memory in order to load the appropriate base object. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-unpack-objects.c | 140 ++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 33 deletions(-) diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c index c6c6368956..e70a71163d 100644 --- a/builtin-unpack-objects.c +++ b/builtin-unpack-objects.c @@ -15,7 +15,7 @@ static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-fil /* We always read in 4kB chunks. */ static unsigned char buffer[4096]; -static unsigned long offset, len; +static unsigned long offset, len, consumed_bytes; static SHA_CTX ctx; /* @@ -51,6 +51,7 @@ static void use(int bytes) die("used more bytes than were available"); len -= bytes; offset += bytes; + consumed_bytes += bytes; } static void *get_data(unsigned long size) @@ -89,35 +90,49 @@ static void *get_data(unsigned long size) struct delta_info { unsigned char base_sha1[20]; + unsigned long base_offset; unsigned long size; void *delta; + unsigned nr; struct delta_info *next; }; static struct delta_info *delta_list; -static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned long size) +static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1, + unsigned long base_offset, + void *delta, unsigned long size) { struct delta_info *info = xmalloc(sizeof(*info)); hashcpy(info->base_sha1, base_sha1); + info->base_offset = base_offset; info->size = size; info->delta = delta; + info->nr = nr; info->next = delta_list; delta_list = info; } -static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size); - -static void write_object(void *buf, unsigned long size, const char *type) -{ +struct obj_info { + unsigned long offset; unsigned char sha1[20]; - if (write_sha1_file(buf, size, type, sha1) < 0) +}; + +static struct obj_info *obj_list; + +static void added_object(unsigned nr, const char *type, void *data, + unsigned long size); + +static void write_object(unsigned nr, void *buf, unsigned long size, + const char *type) +{ + if (write_sha1_file(buf, size, type, obj_list[nr].sha1) < 0) die("failed to write object"); - added_object(sha1, type, buf, size); + added_object(nr, type, buf, size); } -static void resolve_delta(const char *type, +static void resolve_delta(unsigned nr, const char *type, void *base, unsigned long base_size, void *delta, unsigned long delta_size) { @@ -130,20 +145,23 @@ static void resolve_delta(const char *type, if (!result) die("failed to apply delta"); free(delta); - write_object(result, result_size, type); + write_object(nr, result, result_size, type); free(result); } -static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size) +static void added_object(unsigned nr, const char *type, void *data, + unsigned long size) { struct delta_info **p = &delta_list; struct delta_info *info; while ((info = *p) != NULL) { - if (!hashcmp(info->base_sha1, sha1)) { + if (!hashcmp(info->base_sha1, obj_list[nr].sha1) || + info->base_offset == obj_list[nr].offset) { *p = info->next; p = &delta_list; - resolve_delta(type, data, size, info->delta, info->size); + resolve_delta(info->nr, type, data, size, + info->delta, info->size); free(info); continue; } @@ -151,7 +169,8 @@ static void added_object(unsigned char *sha1, const char *type, void *data, unsi } } -static void unpack_non_delta_entry(enum object_type kind, unsigned long size) +static void unpack_non_delta_entry(enum object_type kind, unsigned long size, + unsigned nr) { void *buf = get_data(size); const char *type; @@ -164,30 +183,80 @@ static void unpack_non_delta_entry(enum object_type kind, unsigned long size) default: die("bad type %d", kind); } if (!dry_run && buf) - write_object(buf, size, type); + write_object(nr, buf, size, type); free(buf); } -static void unpack_delta_entry(unsigned long delta_size) +static void unpack_delta_entry(enum object_type kind, unsigned long delta_size, + unsigned nr) { void *delta_data, *base; unsigned long base_size; char type[20]; unsigned char base_sha1[20]; - hashcpy(base_sha1, fill(20)); - use(20); + if (kind == OBJ_REF_DELTA) { + hashcpy(base_sha1, fill(20)); + use(20); + delta_data = get_data(delta_size); + if (dry_run || !delta_data) { + free(delta_data); + return; + } + if (!has_sha1_file(base_sha1)) { + hashcpy(obj_list[nr].sha1, null_sha1); + add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size); + return; + } + } else { + unsigned base_found = 0; + unsigned char *pack, c; + unsigned long base_offset; + unsigned lo, mid, hi; - delta_data = get_data(delta_size); - if (dry_run || !delta_data) { - free(delta_data); - return; + pack = fill(1); + c = *pack; + use(1); + base_offset = c & 127; + while (c & 128) { + base_offset += 1; + if (!base_offset || base_offset & ~(~0UL >> 7)) + die("offset value overflow for delta base object"); + pack = fill(1); + c = *pack; + use(1); + base_offset = (base_offset << 7) + (c & 127); + } + base_offset = obj_list[nr].offset - base_offset; + + delta_data = get_data(delta_size); + if (dry_run || !delta_data) { + free(delta_data); + return; + } + lo = 0; + hi = nr; + while (lo < hi) { + mid = (lo + hi)/2; + if (base_offset < obj_list[mid].offset) { + hi = mid; + } else if (base_offset > obj_list[mid].offset) { + lo = mid + 1; + } else { + hashcpy(base_sha1, obj_list[mid].sha1); + base_found = !is_null_sha1(base_sha1); + break; + } + } + if (!base_found) { + /* 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; + } } - if (!has_sha1_file(base_sha1)) { - add_delta_to_list(base_sha1, delta_data, delta_size); - return; - } base = read_sha1_file(base_sha1, type, &base_size); if (!base) { error("failed to read delta-pack base object %s", @@ -197,7 +266,7 @@ static void unpack_delta_entry(unsigned long delta_size) has_errors = 1; return; } - resolve_delta(type, base, base_size, delta_data, delta_size); + resolve_delta(nr, type, base, base_size, delta_data, delta_size); free(base); } @@ -208,6 +277,8 @@ static void unpack_one(unsigned nr, unsigned total) unsigned long size; enum object_type type; + obj_list[nr].offset = consumed_bytes; + pack = fill(1); c = *pack; use(1); @@ -216,7 +287,7 @@ static void unpack_one(unsigned nr, unsigned total) shift = 4; while (c & 0x80) { pack = fill(1); - c = *pack++; + c = *pack; use(1); size += (c & 0x7f) << shift; shift += 7; @@ -225,13 +296,14 @@ static void unpack_one(unsigned nr, unsigned total) static unsigned long last_sec; static unsigned last_percent; struct timeval now; - unsigned percentage = (nr * 100) / total; + unsigned percentage = ((nr+1) * 100) / total; gettimeofday(&now, NULL); if (percentage != last_percent || now.tv_sec != last_sec) { last_sec = now.tv_sec; last_percent = percentage; - fprintf(stderr, "%4u%% (%u/%u) done\r", percentage, nr, total); + fprintf(stderr, "%4u%% (%u/%u) done\r", + percentage, (nr+1), total); } } switch (type) { @@ -239,10 +311,11 @@ static void unpack_one(unsigned nr, unsigned total) case OBJ_TREE: case OBJ_BLOB: case OBJ_TAG: - unpack_non_delta_entry(type, size); + unpack_non_delta_entry(type, size, nr); return; case OBJ_REF_DELTA: - unpack_delta_entry(size); + case OBJ_OFS_DELTA: + unpack_delta_entry(type, size, nr); return; default: error("bad object type %d", type); @@ -265,9 +338,10 @@ static void unpack_all(void) die("unknown pack file version %d", ntohl(hdr->hdr_version)); fprintf(stderr, "Unpacking %d objects\n", nr_objects); + obj_list = xmalloc(nr_objects * sizeof(*obj_list)); use(sizeof(struct pack_header)); for (i = 0; i < nr_objects; i++) - unpack_one(i+1, nr_objects); + unpack_one(i, nr_objects); if (delta_list) die("unresolved deltas left after unpacking"); } From 53dda6ff6263a3f350514d9edae600468c946ed4 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 21 Sep 2006 00:08:33 -0400 Subject: [PATCH 003/114] teach git-index-pack about deltas with offset to base Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 111 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/index-pack.c b/index-pack.c index aef7f0a32e..fffddd25c9 100644 --- a/index-pack.c +++ b/index-pack.c @@ -18,10 +18,15 @@ struct object_entry unsigned char sha1[20]; }; +union delta_base { + unsigned char sha1[20]; + unsigned long offset; +}; + struct delta_entry { struct object_entry *obj; - unsigned char base_sha1[20]; + union delta_base base; }; static const char *pack_name; @@ -134,13 +139,13 @@ static void *unpack_entry_data(unsigned long offset, static void *unpack_raw_entry(unsigned long offset, enum object_type *obj_type, unsigned long *obj_size, - unsigned char *delta_base, + union delta_base *delta_base, unsigned long *next_obj_offset) { unsigned long pack_limit = pack_size - 20; unsigned long pos = offset; unsigned char c; - unsigned long size; + unsigned long size, base_offset; unsigned shift; enum object_type type; void *data; @@ -161,26 +166,43 @@ static void *unpack_raw_entry(unsigned long offset, case OBJ_REF_DELTA: if (pos + 20 >= pack_limit) bad_object(offset, "object extends past end of pack"); - hashcpy(delta_base, pack_base + pos); + hashcpy(delta_base->sha1, pack_base + pos); pos += 20; - /* fallthru */ + break; + case OBJ_OFS_DELTA: + memset(delta_base, 0, sizeof(*delta_base)); + c = pack_base[pos++]; + base_offset = c & 127; + while (c & 128) { + base_offset += 1; + if (!base_offset || base_offset & ~(~0UL >> 7)) + bad_object(offset, "offset value overflow for delta base object"); + if (pos >= pack_limit) + bad_object(offset, "object extends past end of pack"); + c = pack_base[pos++]; + base_offset = (base_offset << 7) + (c & 127); + } + delta_base->offset = offset - base_offset; + if (delta_base->offset >= offset) + bad_object(offset, "delta base offset is out of bound"); + break; case OBJ_COMMIT: case OBJ_TREE: case OBJ_BLOB: case OBJ_TAG: - data = unpack_entry_data(offset, &pos, size); break; default: bad_object(offset, "bad object type %d", type); } + data = unpack_entry_data(offset, &pos, size); *obj_type = type; *obj_size = size; *next_obj_offset = pos; return data; } -static int find_delta(const unsigned char *base_sha1) +static int find_delta(const union delta_base *base) { int first = 0, last = nr_deltas; @@ -189,7 +211,7 @@ static int find_delta(const unsigned char *base_sha1) struct delta_entry *delta = &deltas[next]; int cmp; - cmp = hashcmp(base_sha1, delta->base_sha1); + cmp = memcmp(base, &delta->base, sizeof(*base)); if (!cmp) return next; if (cmp < 0) { @@ -201,18 +223,18 @@ static int find_delta(const unsigned char *base_sha1) return -first-1; } -static int find_deltas_based_on_sha1(const unsigned char *base_sha1, - int *first_index, int *last_index) +static int find_delta_childs(const union delta_base *base, + int *first_index, int *last_index) { - int first = find_delta(base_sha1); + int first = find_delta(base); int last = first; int end = nr_deltas - 1; if (first < 0) return -1; - while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1)) + while (first > 0 && !memcmp(&deltas[first - 1].base, base, sizeof(*base))) --first; - while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1)) + while (last < end && !memcmp(&deltas[last + 1].base, base, sizeof(*base))) ++last; *first_index = first; *last_index = last; @@ -253,13 +275,13 @@ static void resolve_delta(struct delta_entry *delta, void *base_data, void *result; unsigned long result_size; enum object_type delta_type; - unsigned char base_sha1[20]; + union delta_base delta_base; unsigned long next_obj_offset; int j, first, last; obj->real_type = type; delta_data = unpack_raw_entry(obj->offset, &delta_type, - &delta_size, base_sha1, + &delta_size, &delta_base, &next_obj_offset); result = patch_delta(base_data, base_size, delta_data, delta_size, &result_size); @@ -267,10 +289,22 @@ static void resolve_delta(struct delta_entry *delta, void *base_data, if (!result) bad_object(obj->offset, "failed to apply delta"); sha1_object(result, result_size, type, obj->sha1); - if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) { + + hashcpy(delta_base.sha1, obj->sha1); + if (!find_delta_childs(&delta_base, &first, &last)) { for (j = first; j <= last; j++) - resolve_delta(&deltas[j], result, result_size, type); + if (deltas[j].obj->type == OBJ_REF_DELTA) + resolve_delta(&deltas[j], result, result_size, type); } + + memset(&delta_base, 0, sizeof(delta_base)); + delta_base.offset = obj->offset; + if (!find_delta_childs(&delta_base, &first, &last)) { + for (j = first; j <= last; j++) + if (deltas[j].obj->type == OBJ_OFS_DELTA) + resolve_delta(&deltas[j], result, result_size, type); + } + free(result); } @@ -278,14 +312,14 @@ static int compare_delta_entry(const void *a, const void *b) { const struct delta_entry *delta_a = a; const struct delta_entry *delta_b = b; - return hashcmp(delta_a->base_sha1, delta_b->base_sha1); + return memcmp(&delta_a->base, &delta_b->base, sizeof(union delta_base)); } static void parse_pack_objects(void) { int i; unsigned long offset = sizeof(struct pack_header); - unsigned char base_sha1[20]; + struct delta_entry *delta = deltas; void *data; unsigned long data_size; @@ -299,12 +333,12 @@ static void parse_pack_objects(void) struct object_entry *obj = &objects[i]; obj->offset = offset; data = unpack_raw_entry(offset, &obj->type, &data_size, - base_sha1, &offset); + &delta->base, &offset); obj->real_type = obj->type; - if (obj->type == OBJ_REF_DELTA) { - struct delta_entry *delta = &deltas[nr_deltas++]; + if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) { + nr_deltas++; delta->obj = obj; - hashcpy(delta->base_sha1, base_sha1); + delta++; } else sha1_object(data, data_size, obj->type, obj->sha1); free(data); @@ -312,7 +346,7 @@ static void parse_pack_objects(void) if (offset != pack_size - 20) die("packfile '%s' has junk at the end", pack_name); - /* Sort deltas by base SHA1 for fast searching */ + /* Sort deltas by base SHA1/offset for fast searching */ qsort(deltas, nr_deltas, sizeof(struct delta_entry), compare_delta_entry); @@ -326,22 +360,37 @@ static void parse_pack_objects(void) */ for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; - int j, first, last; + union delta_base base; + int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last; - if (obj->type == OBJ_REF_DELTA) + if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) continue; - if (find_deltas_based_on_sha1(obj->sha1, &first, &last)) + hashcpy(base.sha1, obj->sha1); + ref = !find_delta_childs(&base, &ref_first, &ref_last); + memset(&base, 0, sizeof(base)); + base.offset = obj->offset; + ofs = !find_delta_childs(&base, &ofs_first, &ofs_last); + if (!ref && !ofs) continue; data = unpack_raw_entry(obj->offset, &obj->type, &data_size, - base_sha1, &offset); - for (j = first; j <= last; j++) - resolve_delta(&deltas[j], data, data_size, obj->type); + &base, &offset); + if (ref) + for (j = ref_first; j <= ref_last; j++) + if (deltas[j].obj->type == OBJ_REF_DELTA) + resolve_delta(&deltas[j], data, + data_size, obj->type); + if (ofs) + for (j = ofs_first; j <= ofs_last; j++) + if (deltas[j].obj->type == OBJ_OFS_DELTA) + resolve_delta(&deltas[j], data, + data_size, obj->type); free(data); } /* Check for unresolved deltas */ for (i = 0; i < nr_deltas; i++) { - if (deltas[i].obj->real_type == OBJ_REF_DELTA) + if (deltas[i].obj->real_type == OBJ_REF_DELTA || + deltas[i].obj->real_type == OBJ_OFS_DELTA) die("packfile '%s' has unresolved deltas", pack_name); } } From be6b19145f64f62790049c06320c35011f7312a7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 21 Sep 2006 00:09:44 -0400 Subject: [PATCH 004/114] make git-pack-objects able to create deltas with offset to base This is enabled with --delta-base-offset only, and doesn't work with pack data reuse yet. The idea is to allow for the fetch protocol to use an extension flag to notify the remote end that --delta-base-offset can be used with git-pack-objects. Eventually git-repack will always provide this flag. With this, all delta base objects are now pushed before deltas that depend on them. This is a requirements for OBJ_OFS_DELTA. This is not a requirement for OBJ_REF_DELTA but always doing so makes the code simpler. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 47 +++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index c62734a2a6..221264916d 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -60,6 +60,8 @@ static int non_empty; static int no_reuse_delta; static int local; static int incremental; +static int allow_ofs_delta; + static struct object_entry **sorted_by_sha, **sorted_by_type; static struct object_entry *objects; static int nr_objects, nr_alloc, nr_result; @@ -334,9 +336,6 @@ static unsigned long write_object(struct sha1file *f, enum object_type obj_type; int to_reuse = 0; - if (entry->preferred_base) - return 0; - obj_type = entry->type; if (! entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ @@ -380,18 +379,35 @@ static unsigned long write_object(struct sha1file *f, if (entry->delta) { buf = delta_against(buf, size, entry); size = entry->delta_size; - obj_type = OBJ_REF_DELTA; + obj_type = (allow_ofs_delta && entry->delta->offset) ? + OBJ_OFS_DELTA : OBJ_REF_DELTA; } /* * The object header is a byte of 'type' followed by zero or - * more bytes of length. For deltas, the 20 bytes of delta - * sha1 follows that. + * more bytes of length. */ hdrlen = encode_header(obj_type, size, header); sha1write(f, header, hdrlen); - if (entry->delta) { - sha1write(f, entry->delta, 20); + if (obj_type == OBJ_OFS_DELTA) { + /* + * Deltas with relative base contain an additional + * encoding of the relative offset for the delta + * base from this object's position in the pack. + */ + unsigned long ofs = entry->offset - entry->delta->offset; + unsigned pos = sizeof(header) - 1; + header[pos] = ofs & 127; + while (ofs >>= 7) + header[--pos] = 128 | (--ofs & 127); + sha1write(f, header + pos, sizeof(header) - pos); + hdrlen += sizeof(header) - pos; + } else if (obj_type == OBJ_REF_DELTA) { + /* + * Deltas with a base reference contain + * an additional 20 bytes for the base sha1. + */ + sha1write(f, entry->delta->sha1, 20); hdrlen += 20; } datalen = sha1write_compressed(f, buf, size); @@ -413,7 +429,7 @@ static unsigned long write_object(struct sha1file *f, reused_delta++; reused++; } - if (obj_type == OBJ_REF_DELTA) + if (entry->delta) written_delta++; written++; return hdrlen + datalen; @@ -423,17 +439,16 @@ static unsigned long write_one(struct sha1file *f, struct object_entry *e, unsigned long offset) { - if (e->offset) + if (e->offset || e->preferred_base) /* offset starts from header size and cannot be zero * if it is written already. */ return offset; - e->offset = offset; - offset += write_object(f, e); - /* if we are deltified, write out its base object. */ + /* if we are deltified, write out its base object first. */ if (e->delta) offset = write_one(f, e->delta, offset); - return offset; + e->offset = offset; + return offset + write_object(f, e); } static void write_pack_file(void) @@ -1484,6 +1499,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) no_reuse_delta = 1; continue; } + if (!strcmp("--delta-base-offset", arg)) { + allow_ofs_delta = no_reuse_delta = 1; + continue; + } if (!strcmp("--stdout", arg)) { pack_to_stdout = 1; continue; From 780e6e735be189097dad4b223d8edeb18cce1928 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 22 Sep 2006 21:25:04 -0400 Subject: [PATCH 005/114] make pack data reuse compatible with both delta types This is the missing part to git-pack-objects allowing it to reuse delta data to/from any of the two delta types. It can reuse delta from any type, and it outputs base offsets when --allow-delta-base-offset is provided and the base is also included in the pack. Otherwise it outputs base sha1 references just like it always did. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 206 ++++++++++++++++++++++++++--------------- pack.h | 3 - sha1_file.c | 19 ---- 3 files changed, 131 insertions(+), 97 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 221264916d..6db97b685f 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -29,6 +29,7 @@ struct object_entry { enum object_type type; enum object_type in_pack_type; /* could be delta */ unsigned long delta_size; /* delta data size (uncompressed) */ +#define in_pack_header_size delta_size /* only when reusing pack data */ struct object_entry *delta; /* delta base object */ struct packed_git *in_pack; /* already in pack */ unsigned int in_pack_offset; @@ -86,17 +87,25 @@ static int object_ix_hashsz; * Pack index for existing packs give us easy access to the offsets into * corresponding pack file where each object's data starts, but the entries * do not store the size of the compressed representation (uncompressed - * size is easily available by examining the pack entry header). We build - * a hashtable of existing packs (pack_revindex), and keep reverse index - * here -- pack index file is sorted by object name mapping to offset; this - * pack_revindex[].revindex array is an ordered list of offsets, so if you - * know the offset of an object, next offset is where its packed - * representation ends. + * size is easily available by examining the pack entry header). It is + * also rather expensive to find the sha1 for an object given its offset. + * + * We build a hashtable of existing packs (pack_revindex), and keep reverse + * index here -- pack index file is sorted by object name mapping to offset; + * this pack_revindex[].revindex array is a list of offset/index_nr pairs + * ordered by offset, so if you know the offset of an object, next offset + * is where its packed representation ends and the index_nr can be used to + * get the object sha1 from the main index. */ +struct revindex_entry { + unsigned int offset; + unsigned int nr; +}; struct pack_revindex { struct packed_git *p; - unsigned long *revindex; -} *pack_revindex = NULL; + struct revindex_entry *revindex; +}; +static struct pack_revindex *pack_revindex; static int pack_revindex_hashsz; /* @@ -143,14 +152,9 @@ static void prepare_pack_ix(void) static int cmp_offset(const void *a_, const void *b_) { - unsigned long a = *(unsigned long *) a_; - unsigned long b = *(unsigned long *) b_; - if (a < b) - return -1; - else if (a == b) - return 0; - else - return 1; + const struct revindex_entry *a = a_; + const struct revindex_entry *b = b_; + return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0; } /* @@ -163,25 +167,27 @@ static void prepare_pack_revindex(struct pack_revindex *rix) int i; void *index = p->index_base + 256; - rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1)); + rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1)); for (i = 0; i < num_ent; i++) { unsigned int hl = *((unsigned int *)((char *) index + 24*i)); - rix->revindex[i] = ntohl(hl); + rix->revindex[i].offset = ntohl(hl); + rix->revindex[i].nr = i; } /* This knows the pack format -- the 20-byte trailer * follows immediately after the last object data. */ - rix->revindex[num_ent] = p->pack_size - 20; - qsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset); + rix->revindex[num_ent].offset = p->pack_size - 20; + rix->revindex[num_ent].nr = -1; + qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset); } -static unsigned long find_packed_object_size(struct packed_git *p, - unsigned long ofs) +static struct revindex_entry * find_packed_object(struct packed_git *p, + unsigned int ofs) { int num; int lo, hi; struct pack_revindex *rix; - unsigned long *revindex; + struct revindex_entry *revindex; num = pack_revindex_ix(p); if (num < 0) die("internal error: pack revindex uninitialized"); @@ -193,10 +199,10 @@ static unsigned long find_packed_object_size(struct packed_git *p, hi = num_packed_objects(p) + 1; do { int mi = (lo + hi) / 2; - if (revindex[mi] == ofs) { - return revindex[mi+1] - ofs; + if (revindex[mi].offset == ofs) { + return revindex + mi; } - else if (ofs < revindex[mi]) + else if (ofs < revindex[mi].offset) hi = mi; else lo = mi + 1; @@ -204,6 +210,20 @@ static unsigned long find_packed_object_size(struct packed_git *p, die("internal error: pack revindex corrupt"); } +static unsigned long find_packed_object_size(struct packed_git *p, + unsigned long ofs) +{ + struct revindex_entry *entry = find_packed_object(p, ofs); + return entry[1].offset - ofs; +} + +static unsigned char *find_packed_object_name(struct packed_git *p, + unsigned long ofs) +{ + struct revindex_entry *entry = find_packed_object(p, ofs); + return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4; +} + static void *delta_against(void *buf, unsigned long size, struct object_entry *entry) { unsigned long othersize, delta_size; @@ -249,6 +269,10 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha return n; } +/* + * we are going to reuse the existing object data as is. make + * sure it is not corrupt. + */ static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect) { z_stream stream; @@ -280,32 +304,6 @@ static int check_inflate(unsigned char *data, unsigned long len, unsigned long e return st; } -/* - * we are going to reuse the existing pack entry data. make - * sure it is not corrupt. - */ -static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len) -{ - enum object_type type; - unsigned long size, used; - - if (pack_to_stdout) - return 0; - - /* the caller has already called use_packed_git() for us, - * so it is safe to access the pack data from mmapped location. - * make sure the entry inflates correctly. - */ - used = unpack_object_header_gently(data, len, &type, &size); - if (!used) - return -1; - if (type == OBJ_REF_DELTA) - used += 20; /* skip base object name */ - data += used; - len -= used; - return check_inflate(data, len, entry->size); -} - static int revalidate_loose_object(struct object_entry *entry, unsigned char *map, unsigned long mapsize) @@ -339,7 +337,7 @@ static unsigned long write_object(struct sha1file *f, obj_type = entry->type; if (! entry->in_pack) to_reuse = 0; /* can't reuse what we don't have */ - else if (obj_type == OBJ_REF_DELTA) + else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA) to_reuse = 1; /* check_object() decided it for us */ else if (obj_type != entry->in_pack_type) to_reuse = 0; /* pack has delta which is unusable */ @@ -415,18 +413,38 @@ static unsigned long write_object(struct sha1file *f, } else { struct packed_git *p = entry->in_pack; + + if (entry->delta) { + obj_type = (allow_ofs_delta && entry->delta->offset) ? + OBJ_OFS_DELTA : OBJ_REF_DELTA; + reused_delta++; + } + hdrlen = encode_header(obj_type, entry->size, header); + sha1write(f, header, hdrlen); + if (obj_type == OBJ_OFS_DELTA) { + unsigned long ofs = entry->offset - entry->delta->offset; + unsigned pos = sizeof(header) - 1; + header[pos] = ofs & 127; + while (ofs >>= 7) + header[--pos] = 128 | (--ofs & 127); + sha1write(f, header + pos, sizeof(header) - pos); + hdrlen += sizeof(header) - pos; + } else if (obj_type == OBJ_REF_DELTA) { + sha1write(f, entry->delta->sha1, 20); + hdrlen += 20; + } + use_packed_git(p); - - datalen = find_packed_object_size(p, entry->in_pack_offset); - buf = (char *) p->pack_base + entry->in_pack_offset; - - if (revalidate_pack_entry(entry, buf, datalen)) + buf = (char *) p->pack_base + + entry->in_pack_offset + + entry->in_pack_header_size; + datalen = find_packed_object_size(p, entry->in_pack_offset) + - entry->in_pack_header_size; +//fprintf(stderr, "reusing %d at %d header %d size %d\n", obj_type, entry->in_pack_offset, entry->in_pack_header_size, datalen); + if (!pack_to_stdout && check_inflate(buf, datalen, entry->size)) die("corrupt delta in pack %s", sha1_to_hex(entry->sha1)); sha1write(f, buf, datalen); unuse_packed_git(p); - hdrlen = 0; /* not really */ - if (obj_type == OBJ_REF_DELTA) - reused_delta++; reused++; } if (entry->delta) @@ -914,26 +932,64 @@ static void check_object(struct object_entry *entry) char type[20]; if (entry->in_pack && !entry->preferred_base) { - unsigned char base[20]; - unsigned long size; - struct object_entry *base_entry; + struct packed_git *p = entry->in_pack; + unsigned long left = p->pack_size - entry->in_pack_offset; + unsigned long size, used; + unsigned char *buf; + struct object_entry *base_entry = NULL; + + use_packed_git(p); + buf = p->pack_base; + buf += entry->in_pack_offset; /* We want in_pack_type even if we do not reuse delta. * There is no point not reusing non-delta representations. */ - check_reuse_pack_delta(entry->in_pack, - entry->in_pack_offset, - base, &size, - &entry->in_pack_type); + used = unpack_object_header_gently(buf, left, + &entry->in_pack_type, &size); + if (!used || left - used <= 20) + die("corrupt pack for %s", sha1_to_hex(entry->sha1)); /* Check if it is delta, and the base is also an object * we are going to pack. If so we will reuse the existing * delta. */ - if (!no_reuse_delta && - entry->in_pack_type == OBJ_REF_DELTA && - (base_entry = locate_object_entry(base)) && - (!base_entry->preferred_base)) { + if (!no_reuse_delta) { + unsigned char c, *base_name; + unsigned long ofs; + /* there is at least 20 bytes left in the pack */ + switch (entry->in_pack_type) { + case OBJ_REF_DELTA: + base_name = buf + used; + used += 20; + break; + case OBJ_OFS_DELTA: + c = buf[used++]; + ofs = c & 127; + while (c & 128) { + ofs += 1; + if (!ofs || ofs & ~(~0UL >> 7)) + die("delta base offset overflow in pack for %s", + sha1_to_hex(entry->sha1)); + c = buf[used++]; + 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)); + ofs = entry->in_pack_offset - ofs; + base_name = find_packed_object_name(p, ofs); + break; + default: + base_name = NULL; + } + if (base_name) + base_entry = locate_object_entry(base_name); + } + unuse_packed_git(p); + entry->in_pack_header_size = used; + + if (base_entry && !base_entry->preferred_base) { /* Depth value does not matter - find_deltas() * will never consider reused delta as the @@ -942,9 +998,9 @@ static void check_object(struct object_entry *entry) */ /* uncompressed size of the delta data */ - entry->size = entry->delta_size = size; + entry->size = size; entry->delta = base_entry; - entry->type = OBJ_REF_DELTA; + entry->type = entry->in_pack_type; entry->delta_sibling = base_entry->delta_child; base_entry->delta_child = entry; @@ -1500,7 +1556,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) continue; } if (!strcmp("--delta-base-offset", arg)) { - allow_ofs_delta = no_reuse_delta = 1; + allow_ofs_delta = 1; continue; } if (!strcmp("--stdout", arg)) { diff --git a/pack.h b/pack.h index 05557da152..4c9bddd4f2 100644 --- a/pack.h +++ b/pack.h @@ -16,7 +16,4 @@ struct pack_header { }; extern int verify_pack(struct packed_git *, int); -extern int check_reuse_pack_delta(struct packed_git *, unsigned long, - unsigned char *, unsigned long *, - enum object_type *); #endif diff --git a/sha1_file.c b/sha1_file.c index fdb4588280..18c2f88112 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1000,25 +1000,6 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of return offset + used; } -int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, - unsigned char *base, unsigned long *sizep, - enum object_type *kindp) -{ - unsigned long ptr; - int status = -1; - - use_packed_git(p); - ptr = offset; - ptr = unpack_object_header(p, ptr, kindp, sizep); - if (*kindp != OBJ_REF_DELTA) - goto done; - hashcpy(base, (unsigned char *) p->pack_base + ptr); - status = 0; - done: - unuse_packed_git(p); - return status; -} - void packed_object_info_detail(struct packed_git *p, unsigned long offset, char *type, From e4fe4b8ef7cdde842a9e5e2594d0fba1367d9dd3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 26 Sep 2006 11:27:39 -0400 Subject: [PATCH 006/114] let the GIT native protocol use offsets to delta base when possible There is no reason not to always do this when both ends agree. Therefore a client that can accept offsets to delta base always sends the "ofs-delta" flag. The server will stream a pack with or without offset to delta base depending on whether that flag is provided or not with no additional cost. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- fetch-pack.c | 5 +++-- upload-pack.c | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fetch-pack.c b/fetch-pack.c index e8708aa802..474d54520e 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -166,12 +166,13 @@ static int find_common(int fd[2], unsigned char *result_sha1, } if (!fetching) - packet_write(fd[1], "want %s%s%s%s%s\n", + packet_write(fd[1], "want %s%s%s%s%s%s\n", sha1_to_hex(remote), (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), - (use_thin_pack ? " thin-pack" : "")); + (use_thin_pack ? " thin-pack" : ""), + " ofs-delta"); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; diff --git a/upload-pack.c b/upload-pack.c index 189b239cc0..9ec3775049 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -16,7 +16,7 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n #define OUR_REF (1U << 1) #define WANTED (1U << 2) static int multi_ack, nr_our_refs; -static int use_thin_pack; +static int use_thin_pack, use_ofs_delta; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -137,7 +137,9 @@ static void create_pack_file(void) close(pu_pipe[1]); close(pe_pipe[0]); close(pe_pipe[1]); - execl_git_cmd("pack-objects", "--stdout", "--progress", NULL); + execl_git_cmd("pack-objects", "--stdout", "--progress", + use_ofs_delta ? "--delta-base-offset" : NULL, + NULL); kill(pid_rev_list, SIGKILL); die("git-upload-pack: unable to exec git-pack-objects"); } @@ -393,6 +395,8 @@ static void receive_needs(void) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; + if (strstr(line+45, "ofs-delta")) + use_ofs_delta = 1; if (strstr(line+45, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (strstr(line+45, "side-band")) @@ -418,7 +422,7 @@ static void receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1) { - static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; + static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta"; struct object *o = parse_object(sha1); if (!o) From f130446920b550a69716346fb9a9947c04fc7f90 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 27 Sep 2006 15:30:21 -0400 Subject: [PATCH 007/114] zap a debug remnant Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 6db97b685f..16e98f3f3f 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -440,7 +440,6 @@ static unsigned long write_object(struct sha1file *f, + entry->in_pack_header_size; datalen = find_packed_object_size(p, entry->in_pack_offset) - entry->in_pack_header_size; -//fprintf(stderr, "reusing %d at %d header %d size %d\n", obj_type, entry->in_pack_offset, entry->in_pack_header_size, datalen); if (!pack_to_stdout && check_inflate(buf, datalen, entry->size)) die("corrupt delta in pack %s", sha1_to_hex(entry->sha1)); sha1write(f, buf, datalen); From a2700696995651322796e04092bf4a4bfed31b88 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 27 Sep 2006 15:42:16 -0400 Subject: [PATCH 008/114] allow delta data reuse even if base object is a preferred base Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 16e98f3f3f..ee5f031bc2 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -988,7 +988,7 @@ static void check_object(struct object_entry *entry) unuse_packed_git(p); entry->in_pack_header_size = used; - if (base_entry && !base_entry->preferred_base) { + if (base_entry) { /* Depth value does not matter - find_deltas() * will never consider reused delta as the From 0ab7befa31d07fe3ffb51a6cc626d4c09ded1c92 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 17:50:52 -0700 Subject: [PATCH 009/114] grep --all-match This lets you say: git grep --all-match -e A -e B -e C to find lines that match A or B or C but limit the matches from the files that have all of A, B and C. This is different from git grep -e A --and -e B --and -e C in that the latter looks for a single line that has all of these at the same time. Signed-off-by: Junio C Hamano --- Documentation/git-grep.txt | 11 +++- builtin-grep.c | 4 ++ grep.c | 114 +++++++++++++++++++++++++++++++------ grep.h | 2 + 4 files changed, 112 insertions(+), 19 deletions(-) diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index d8af4d961b..bfbece9864 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -14,7 +14,7 @@ SYNOPSIS [-v | --invert-match] [-h|-H] [--full-name] [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings] [-n] [-l | --files-with-matches] [-L | --files-without-match] - [-c | --count] + [-c | --count] [--all-match] [-A ] [-B ] [-C ] [-f ] [-e] [--and|--or|--not|(|)|-e ...] [...] @@ -96,6 +96,11 @@ OPTIONS higher precedence than `--or`. `-e` has to be used for all patterns. +--all-match:: + When giving multiple pattern expressions combined with `--or`, + this flag is specified to limit the match to files that + have lines to match all of them. + `...`:: Search blobs in the trees for specified patterns. @@ -111,6 +116,10 @@ git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \):: Looks for a line that has `#define` and either `MAX_PATH` or `PATH_MAX`. +git grep --all-match -e NODE -e Unexpected:: + Looks for a line that has `NODE` or `Unexpected` in + files that have lines that match both. + Author ------ Originally written by Linus Torvalds , later diff --git a/builtin-grep.c b/builtin-grep.c index 4205e5d38d..ad7dc00cde 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -596,6 +596,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix) GREP_CLOSE_PAREN); continue; } + if (!strcmp("--all-match", arg)) { + opt.all_match = 1; + continue; + } if (!strcmp("-e", arg)) { if (1 < argc) { append_grep_pattern(&opt, argv[1], diff --git a/grep.c b/grep.c index c411ddd4d5..0fc078ec0a 100644 --- a/grep.c +++ b/grep.c @@ -34,7 +34,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) } } -static struct grep_expr *compile_pattern_expr(struct grep_pat **); +static struct grep_expr *compile_pattern_or(struct grep_pat **); static struct grep_expr *compile_pattern_atom(struct grep_pat **list) { struct grep_pat *p; @@ -52,7 +52,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list) return x; case GREP_OPEN_PAREN: *list = p->next; - x = compile_pattern_expr(list); + x = compile_pattern_or(list); if (!x) return NULL; if (!*list || (*list)->token != GREP_CLOSE_PAREN) @@ -138,6 +138,9 @@ void compile_grep_patterns(struct grep_opt *opt) { struct grep_pat *p; + if (opt->all_match) + opt->extended = 1; + for (p = opt->pattern_list; p; p = p->next) { switch (p->token) { case GREP_PATTERN: /* atom */ @@ -309,40 +312,63 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol return hit; } -static int match_expr_eval(struct grep_opt *opt, +static int match_expr_eval(struct grep_opt *o, struct grep_expr *x, char *bol, char *eol, - enum grep_context ctx) + enum grep_context ctx, + int collect_hits) { + int h = 0; + switch (x->node) { case GREP_NODE_ATOM: - return match_one_pattern(opt, x->u.atom, bol, eol, ctx); + h = match_one_pattern(o, x->u.atom, bol, eol, ctx); break; case GREP_NODE_NOT: - return !match_expr_eval(opt, x->u.unary, bol, eol, ctx); + h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); + break; case GREP_NODE_AND: - return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) && - match_expr_eval(opt, x->u.binary.right, bol, eol, ctx)); + if (!collect_hits) + return (match_expr_eval(o, x->u.binary.left, + bol, eol, ctx, 0) && + match_expr_eval(o, x->u.binary.right, + bol, eol, ctx, 0)); + h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); + h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); + break; case GREP_NODE_OR: - return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) || - match_expr_eval(opt, x->u.binary.right, bol, eol, ctx)); + if (!collect_hits) + return (match_expr_eval(o, x->u.binary.left, + bol, eol, ctx, 0) || + match_expr_eval(o, x->u.binary.right, + bol, eol, ctx, 0)); + h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); + x->u.binary.left->hit |= h; + h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); + break; + default: + die("Unexpected node type (internal error) %d\n", x->node); } - die("Unexpected node type (internal error) %d\n", x->node); + if (collect_hits) + x->hit |= h; + return h; } static int match_expr(struct grep_opt *opt, char *bol, char *eol, - enum grep_context ctx) + enum grep_context ctx, int collect_hits) { struct grep_expr *x = opt->pattern_expression; - return match_expr_eval(opt, x, bol, eol, ctx); + return match_expr_eval(opt, x, bol, eol, ctx, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, - enum grep_context ctx) + enum grep_context ctx, int collect_hits) { struct grep_pat *p; if (opt->extended) - return match_expr(opt, bol, eol, ctx); + return match_expr(opt, bol, eol, ctx, collect_hits); + + /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { if (match_one_pattern(opt, p, bol, eol, ctx)) return 1; @@ -350,7 +376,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, return 0; } -int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size) +static int grep_buffer_1(struct grep_opt *opt, const char *name, + char *buf, unsigned long size, int collect_hits) { char *bol = buf; unsigned long left = size; @@ -386,7 +413,7 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long while (left) { char *eol, ch; - int hit = 0; + int hit; eol = end_of_line(bol, &left); ch = *eol; @@ -395,9 +422,12 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; - hit = match_line(opt, bol, eol, ctx); + hit = match_line(opt, bol, eol, ctx, collect_hits); *eol = ch; + if (collect_hits) + goto next_line; + /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. @@ -477,6 +507,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long } free(prev); + if (collect_hits) + return 0; if (opt->status_only) return 0; @@ -496,3 +528,49 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long return !!last_hit; } +static void clr_hit_marker(struct grep_expr *x) +{ + /* All-hit markers are meaningful only at the very top level + * OR node. + */ + while (1) { + x->hit = 0; + if (x->node != GREP_NODE_OR) + return; + x->u.binary.left->hit = 0; + x = x->u.binary.right; + } +} + +static int chk_hit_marker(struct grep_expr *x) +{ + /* Top level nodes have hit markers. See if they all are hits */ + while (1) { + if (x->node != GREP_NODE_OR) + return x->hit; + if (!x->u.binary.left->hit) + return 0; + x = x->u.binary.right; + } +} + +int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size) +{ + /* + * we do not have to do the two-pass grep when we do not check + * buffer-wide "all-match". + */ + if (!opt->all_match) + return grep_buffer_1(opt, name, buf, size, 0); + + /* Otherwise the toplevel "or" terms hit a bit differently. + * We first clear hit markers from them. + */ + clr_hit_marker(opt->pattern_expression); + grep_buffer_1(opt, name, buf, size, 1); + + if (!chk_hit_marker(opt->pattern_expression)) + return 0; + + return grep_buffer_1(opt, name, buf, size, 0); +} diff --git a/grep.h b/grep.h index af9098cfe8..d252dd25f8 100644 --- a/grep.h +++ b/grep.h @@ -35,6 +35,7 @@ enum grep_expr_node { struct grep_expr { enum grep_expr_node node; + unsigned hit; union { struct grep_pat *atom; struct grep_expr *unary; @@ -59,6 +60,7 @@ struct grep_opt { unsigned count:1; unsigned word_regexp:1; unsigned fixed:1; + unsigned all_match:1; #define GREP_BINARY_DEFAULT 0 #define GREP_BINARY_NOMATCH 1 #define GREP_BINARY_TEXT 2 From 70d0afba914ddcf8c8a0144e5c6519d43d641dd9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 17:55:58 -0700 Subject: [PATCH 010/114] teach revision walker about --all-match. This lets you say: git log --all-match --author=Linus --committer=Junio --grep=rev-list to limit commits that was written by Linus, committed by me and the log message contains word "rev-list". Signed-off-by: Junio C Hamano --- revision.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/revision.c b/revision.c index 93f25130a0..f1e0caaae3 100644 --- a/revision.c +++ b/revision.c @@ -732,6 +732,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch int i, flags, seen_dashdash, show_merge; const char **unrecognized = argv + 1; int left = 1; + int all_match = 0; /* First, search for "--" */ seen_dashdash = 0; @@ -967,6 +968,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch add_message_grep(revs, arg+7); continue; } + if (!strcmp(arg, "--all-match")) { + all_match = 1; + continue; + } opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { @@ -1028,8 +1033,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (diff_setup_done(&revs->diffopt) < 0) die("diff_setup_done failed"); - if (revs->grep_filter) + if (revs->grep_filter) { + revs->grep_filter->all_match = all_match; compile_grep_patterns(revs->grep_filter); + } return left; } From ce74618d95088d99de53fb691f361da05932f705 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Sep 2006 16:17:58 -0700 Subject: [PATCH 011/114] git-diff/git-apply: make diff output a bit friendlier to GNU patch (part 1) Somebody was wondering on #git channel why a git generated diff does not apply with GNU patch when the filename contains a SP. It is because GNU patch expects to find TAB (and trailing timestamp) on ---/+++ (old_name and new_name) lines after the filenames. The "diff --git" output format was carefully designed to be compatible with GNU patch where it can, but whitespace characters were always a pain. We can make our output a bit more GNU patch friendly by adding an extra TAB (but not trailing timestamp) to old/new name lines when the filename as a SP in it. This updates git-apply to prepare ourselves to accept such a patch, but we still do not generate output that is patch friendly yet. That change needs to wait until everybody has this change. When a filename contains a real tab, "diff --git" format always c-quotes it as discussed on the list with GNU patch maintainer previously: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 so there should be no downside. Signed-off-by: Junio C Hamano --- builtin-apply.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index de5f855266..a7317d7544 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -360,7 +360,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew) { if (!orig_name && !isnull) - return find_name(line, NULL, 1, 0); + return find_name(line, NULL, 1, TERM_TAB); if (orig_name) { int len; @@ -370,7 +370,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, len = strlen(name); if (isnull) die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr); - another = find_name(line, NULL, 1, 0); + another = find_name(line, NULL, 1, TERM_TAB); if (!another || memcmp(another, name, len)) die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr); free(another); From 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=1B=2CAi=1B=28Bjar?= Date: Sun, 1 Oct 2006 05:33:05 +0200 Subject: [PATCH 012/114] fetch: Misc output cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular it removes duplicate information, uses short hashes (as git-log and company) and uses .. for fast forwarding commits and ... for not-fast-forwarding commits (shorter, easier to copy&paste). It also reformat the output as: 1. the ones we store in our local ref (either branches or tags): 1a) fast-forward * refs/heads/origin: fast forward to branch 'master' of ../git/ old..new: 1ad7a06..bc1a580 1b) same (only shown under -v) * refs/heads/next: same as branch 'origin/next' of ../git/ commit: ce47b9f 1c) non-fast-forward, forced * refs/heads/pu: forcing update to non-fast forward branch 'pu' of ../git/ old...new: 7c733a8...5faa935 1d) non-fast-forward, did not update because not forced * refs/heads/po: not updating to non-fast forward branch 'po' of ../git/ old...new: 7c733a8...5faa935 1e) creating a new local ref to store * refs/tags/v1.4.2-rc4: storing tag 'v1.4.2-rc4' of ../git/ tag: 8c7a107 * refs/heads/next: storing branch 'next' of ../git/ commit: f8a20ae 2. the ones we do not store in our local ref (only shown under -v): * fetched branch 'master' of ../git commit: 695dffe * fetched tag 'v1.4.2-rc4' of ../git tag: 8c7a107 Signed-off-by: Santi B.ANijar Signed-off-by: Junio C Hamano --- git-fetch.sh | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/git-fetch.sh b/git-fetch.sh index f1522bd49a..85e96a1cc7 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -129,22 +129,25 @@ append_fetch_head () { then headc_=$(git-rev-parse --verify "$head_^0") || exit echo "$headc_ $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD" - [ "$verbose" ] && echo >&2 "* committish: $head_" - [ "$verbose" ] && echo >&2 " $note_" else echo "$head_ not-for-merge $note_" >>"$GIT_DIR/FETCH_HEAD" - [ "$verbose" ] && echo >&2 "* non-commit: $head_" - [ "$verbose" ] && echo >&2 " $note_" - fi - if test "$local_name_" != "" - then - # We are storing the head locally. Make sure that it is - # a fast forward (aka "reverse push"). - fast_forward_local "$local_name_" "$head_" "$note_" fi + + update_local_ref "$local_name_" "$head_" "$note_" } -fast_forward_local () { +update_local_ref () { + # If we are storing the head locally make sure that it is + # a fast forward (aka "reverse push"). + + label_=$(git-cat-file -t $2) + newshort_=$(git-rev-parse --short $2) + if test -z "$1" ; then + [ "$verbose" ] && echo >&2 "* fetched $3" + [ "$verbose" ] && echo >&2 " $label_: $newshort_" + return 0 + fi + oldshort_=$(git-rev-parse --short "$1" 2>/dev/null) mkdir -p "$(dirname "$GIT_DIR/$1")" case "$1" in refs/tags/*) @@ -154,13 +157,16 @@ fast_forward_local () { then if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2" then - [ "$verbose" ] && echo >&2 "* $1: same as $3" ||: + [ "$verbose" ] && echo >&2 "* $1: same as $3" + [ "$verbose" ] && echo >&2 " $label_: $newshort_" ||: else echo >&2 "* $1: updating with $3" + echo >&2 " $label_: $newshort_" git-update-ref -m "$rloga: updating tag" "$1" "$2" fi else echo >&2 "* $1: storing $3" + echo >&2 " $label_: $newshort_" git-update-ref -m "$rloga: storing tag" "$1" "$2" fi ;; @@ -178,31 +184,34 @@ fast_forward_local () { if test -n "$verbose" then echo >&2 "* $1: same as $3" + echo >&2 " $label_: $newshort_" fi ;; *,$local) echo >&2 "* $1: fast forward to $3" - echo >&2 " from $local to $2" + echo >&2 " old..new: $oldshort_..$newshort_" git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local" ;; *) false ;; esac || { - echo >&2 "* $1: does not fast forward to $3;" case ",$force,$single_force," in *,t,*) - echo >&2 " forcing update." + echo >&2 "* $1: forcing update to non-fast forward $3" + echo >&2 " old...new: $oldshort_...$newshort_" git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local" ;; *) - echo >&2 " not updating." + echo >&2 "* $1: not updating to non-fast forward $3" + echo >&2 " old...new: $oldshort_...$newshort_" exit 1 ;; esac } else echo >&2 "* $1: storing $3" + echo >&2 " $label_: $newshort_" git-update-ref -m "$rloga: storing head" "$1" "$2" fi ;; From ba0ac36ec5708820e670731001f7ab35351c6c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=C3=A9jar?= Date: Sun, 1 Oct 2006 05:34:17 +0200 Subject: [PATCH 013/114] merge and resolve: Output short hashes and .. in "Updating ..." MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- git-merge.sh | 2 +- git-resolve.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 5b34b4de99..49c46d55df 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -197,7 +197,7 @@ f,*) ;; ?,1,"$head",*) # Again the most common case of merging one remote. - echo "Updating from $head to $1" + echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)" git-update-index --refresh 2>/dev/null new_head=$(git-rev-parse --verify "$1^0") && git-read-tree -u -v -m $head "$new_head" && diff --git a/git-resolve.sh b/git-resolve.sh index 729ec65dc9..36b90e3849 100755 --- a/git-resolve.sh +++ b/git-resolve.sh @@ -46,7 +46,7 @@ case "$common" in exit 0 ;; "$head") - echo "Updating from $head to $merge" + echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)" git-read-tree -u -m $head $merge || exit 1 git-update-ref -m "resolve $merge_name: Fast forward" \ HEAD "$merge" "$head" From 9e756904d05a53f1f519492ea1addc3e3163c678 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Sun, 1 Oct 2006 23:57:48 +0200 Subject: [PATCH 014/114] gitweb: start to generate PATH_INFO URLs. Instead of providing the project as a ?p= parameter it is simply appended to the base URI. All other parameters are appended to that, except for ?a=summary which is the default and can be omitted. The this can be enabled with the "pathinfo" feature in gitweb_config.perl. [jc: let's introduce new features disabled by default not to upset too many existing installations.] Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 44991b1538..10e803a97f 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -102,6 +102,10 @@ our %feature = ( 'sub' => \&feature_pickaxe, 'override' => 0, 'default' => [1]}, + + 'pathinfo' => { + 'override' => 0, + 'default' => [0]}, ); sub gitweb_check_feature { @@ -375,6 +379,7 @@ exit; sub href(%) { my %params = @_; + my $href = $my_uri; my @mapping = ( project => "p", @@ -393,6 +398,19 @@ sub href(%) { $params{'project'} = $project unless exists $params{'project'}; + my ($use_pathinfo) = gitweb_check_feature('pathinfo'); + if ($use_pathinfo) { + # use PATH_INFO for project name + $href .= "/$params{'project'}" if defined $params{'project'}; + delete $params{'project'}; + + # Summary just uses the project path URL + if (defined $params{'action'} && $params{'action'} eq 'summary') { + delete $params{'action'}; + } + } + + # now encode the parameters explicitly my @result = (); for (my $i = 0; $i < @mapping; $i += 2) { my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]); @@ -400,7 +418,9 @@ sub href(%) { push @result, $symbol . "=" . esc_param($params{$name}); } } - return "$my_uri?" . join(';', @result); + $href .= "?" . join(';', @result) if scalar @result; + + return $href; } From 604cb211a9b5401b265d1c1ca3d57dad243e7260 Mon Sep 17 00:00:00 2001 From: Alan Chandler Date: Tue, 3 Oct 2006 22:48:46 +0100 Subject: [PATCH 015/114] Update the gitweb/README file to include setting the GITWEB_CONFIG environment Signed-off-by: Alan Chandler Signed-off-by: Junio C Hamano --- gitweb/README | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gitweb/README b/gitweb/README index 61c7ab5d4b..78e6fc05f3 100644 --- a/gitweb/README +++ b/gitweb/README @@ -43,6 +43,7 @@ repositories, you can configure apache like this: DocumentRoot /pub/git RewriteEngine on RewriteRule ^/(.*\.git/(?!/?(info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] + SetEnv GITWEB_CONFIG /etc/gitweb.conf The above configuration expects your public repositories to live under @@ -51,6 +52,12 @@ both as cloneable GIT URL and as browseable gitweb interface. If you then start your git-daemon with --base-path=/pub/git --export-all then you can even use the git:// URL with exactly the same path. +Setting the environment variable GITWEB_CONFIG will tell gitweb to use +the named file (i.e. in this example /etc/gitweb.conf) as a +configuration for gitweb. Perl variables defined in here will +override the defaults given at the head of the gitweb.perl (or +gitweb.cgi). Look at the comments in that file for information on +which variables and what they mean. Originally written by: From 281e67d6fa8e523147792c17fbe6db03f13f72e1 Mon Sep 17 00:00:00 2001 From: Alan Chandler Date: Tue, 3 Oct 2006 21:11:25 +0100 Subject: [PATCH 016/114] Fix usage string to match that given in the man page Still not managed to understand git-send-mail sufficiently well to not accidently miss of this list when I sending it to Junio Signed-off-by: Alan Chandler Signed-off-by: Junio C Hamano --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index ae80e78456..b8e8622afb 100644 --- a/git.c +++ b/git.c @@ -16,7 +16,7 @@ #include "builtin.h" const char git_usage_string[] = - "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]"; + "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]"; static void prepend_to_path(const char *dir, int len) { From c065b6e4293f856012ec53fcff7bf4d47ac50927 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Tue, 3 Oct 2006 18:38:25 +0200 Subject: [PATCH 017/114] git-commit: cleanup unused function. The report() function is not used anymore. Kill it. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- git-commit.sh | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/git-commit.sh b/git-commit.sh index 5a4c659b6f..6f6cbda898 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -32,33 +32,6 @@ save_index () { cp -p "$THIS_INDEX" "$NEXT_INDEX" } -report () { - header="# -# $1: -# ($2) -# -" - trailer="" - while read status name newname - do - printf '%s' "$header" - header="" - trailer="# -" - case "$status" in - M ) echo "# modified: $name";; - D*) echo "# deleted: $name";; - T ) echo "# typechange: $name";; - C*) echo "# copied: $name -> $newname";; - R*) echo "# renamed: $name -> $newname";; - A*) echo "# new file: $name";; - U ) echo "# unmerged: $name";; - esac - done - printf '%s' "$trailer" - [ "$header" ] -} - run_status () { # If TMP_INDEX is defined, that means we are doing # "--only" partial commit, and that index file is used From 91b489776c25866568c081e8cf4ae83fa41f8707 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 3 Oct 2006 17:29:26 +0100 Subject: [PATCH 018/114] git-rebase: Use --ignore-if-in-upstream option when executing git-format-patch. This reduces the number of conflicts when rebasing after a series of patches to the same piece of code is committed upstream. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- git-rebase.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-rebase.sh b/git-rebase.sh index a7373c0532..413636e208 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -286,7 +286,7 @@ fi if test -z "$do_merge" then - git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD | + git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD | git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \ --reflog-action=rebase exit $? From b758789c202dc491555b698149afe4d4941f7cd5 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 3 Oct 2006 17:29:31 +0100 Subject: [PATCH 019/114] git-rebase: Add a -v option to show a diffstat of the changes upstream at the start of a rebase. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- Documentation/git-rebase.txt | 5 ++++- git-rebase.sh | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 9d7bcaa38c..10f2924f4d 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head SYNOPSIS -------- -'git-rebase' [--merge] [--onto ] [] +'git-rebase' [-v] [--merge] [--onto ] [] 'git-rebase' --continue | --skip | --abort @@ -121,6 +121,9 @@ OPTIONS is used instead (`git-merge-recursive` when merging a single head, `git-merge-octopus` otherwise). This implies --merge. +-v, \--verbose:: + Display a diffstat of what changed upstream since the last rebase. + include::merge-strategies.txt[] NOTES diff --git a/git-rebase.sh b/git-rebase.sh index 413636e208..546fa446fc 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--onto ] []' +USAGE='[-v] [--onto ] []' LONG_USAGE='git-rebase replaces with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to , otherwise it is equal to @@ -39,6 +39,7 @@ strategy=recursive do_merge= dotest=$GIT_DIR/.dotest-merge prec=4 +verbose= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -190,6 +191,9 @@ do esac do_merge=t ;; + -v|--verbose) + verbose=t + ;; -*) usage ;; @@ -273,6 +277,12 @@ then exit 0 fi +if test -n "$verbose" +then + echo "Changes from $mb to $onto:" + git-diff-tree --stat --summary "$mb" "$onto" +fi + # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD. git-reset --hard "$onto" From a9455919064ab316b7852d0006c1bd771b727f2e Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Tue, 3 Oct 2006 20:07:43 +0200 Subject: [PATCH 020/114] gitweb: warn if feature cannot be overridden. If the administrator configures pathinfo to be overrideable by the local repository a warning is shown. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 10e803a97f..0ff6f7c1e4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -116,6 +116,10 @@ sub gitweb_check_feature { $feature{$name}{'override'}, @{$feature{$name}{'default'}}); if (!$override) { return @defaults; } + if (!defined $sub) { + warn "feature $name is not overrideable"; + return @defaults; + } return $sub->(@defaults); } From 6030649591256a8bc1b504ec1e4fce86ab0ad064 Mon Sep 17 00:00:00 2001 From: Franck Bui-Huu Date: Thu, 5 Oct 2006 10:42:16 +0200 Subject: [PATCH 021/114] Add git-upload-archive to the main git man page Signed-off-by: Franck Bui-Huu Signed-off-by: Junio C Hamano --- Documentation/git.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/git.txt b/Documentation/git.txt index 2135b65516..3af6fc63e2 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -243,6 +243,9 @@ gitlink:git-update-server-info[1]:: Updates auxiliary information on a dumb server to help clients discover references and packs on it. +gitlink:git-upload-archive[1]:: + Invoked by 'git-archive' to send a generated archive. + gitlink:git-upload-pack[1]:: Invoked by 'git-fetch-pack' to push what are asked for. From 9ccb64c8e0791a865ad520bcfff4b02cc7c50097 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 02:26:12 -0700 Subject: [PATCH 022/114] tar-tree deprecation: we eat our own dog food. It is silly to keep using git-tar-tree in dist target when the command gives a big deprecation warning when called. Instead, use "git-archive --format=tar" which we recommend to our users. Update gitweb's snapshot feature to use git-archive for the same reason. Signed-off-by: Junio C Hamano --- Makefile | 5 +++-- gitweb/gitweb.perl | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 401b893bfa..2c7c33855e 100644 --- a/Makefile +++ b/Makefile @@ -856,8 +856,9 @@ git.spec: git.spec.in mv $@+ $@ GIT_TARNAME=git-$(GIT_VERSION) -dist: git.spec git-tar-tree - ./git-tar-tree HEAD^{tree} $(GIT_TARNAME) > $(GIT_TARNAME).tar +dist: git.spec git-archive + ./git-archive --format=tar \ + --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar @mkdir -p $(GIT_TARNAME) @cp git.spec $(GIT_TARNAME) @echo $(GIT_VERSION) > $(GIT_TARNAME)/version diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3e9d4a0052..6e1496d6ac 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2838,9 +2838,12 @@ sub git_snapshot { -content_disposition => 'inline; filename="' . "$filename" . '"', -status => '200 OK'); - my $git_command = git_cmd_str(); - open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or - die_error(undef, "Execute git-tar-tree failed."); + my $git = git_cmd_str(); + my $name = $project; + $name =~ s/\047/\047\\\047\047/g; + open my $fd, "-|", + "$git archive --format=tar --prefix=\'$name\'/ $hash | $command" + or die_error(undef, "Execute git-tar-tree failed."); binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi From c530c5aa31f44adafd1f4ecb05223024162e689c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 5 Oct 2006 11:29:57 +0200 Subject: [PATCH 023/114] git.el: Fixed inverted "renamed from/to" message. The deleted file should be labeled "renamed to" and the added file "renamed from", not the other way around (duh!) Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 68de9be0c7..5354cd67b3 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -422,8 +422,8 @@ and returns the process output as a string." (propertize (concat " (" (if (eq state 'copy) "copied from " - (if (eq (git-fileinfo->state info) 'added) "renamed to " - "renamed from ")) + (if (eq (git-fileinfo->state info) 'added) "renamed from " + "renamed to ")) (git-escape-file-name (git-fileinfo->orig-name info)) ")") 'face 'git-status-face) ""))) From 13f8e0b24b546c0da942ab8ebe334d6e55fe1ea6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 5 Oct 2006 11:30:44 +0200 Subject: [PATCH 024/114] vc-git.el: Switch to using git-blame instead of git-annotate. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 4a8f79092d..4189c4ced0 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -119,10 +119,10 @@ (defun vc-git-annotate-command (file buf &optional rev) ; FIXME: rev is ignored (let ((name (file-relative-name file))) - (call-process "git" nil buf nil "annotate" name))) + (call-process "git" nil buf nil "blame" name))) (defun vc-git-annotate-time () - (and (re-search-forward "[0-9a-f]+\t(.*\t\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\)\t[0-9]+)" nil t) + (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t) (vc-annotate-convert-time (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7)))))) From 6e0e92fda893311ff5af91836e5007bf6bbd4a21 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 5 Oct 2006 12:22:57 -0700 Subject: [PATCH 025/114] gitweb: Do not print "log" and "shortlog" redundantly in commit view Do not print "log" and "shortlog" redundantly in commit view. This is passed into the $extra argument of git_print_page_nav from git_commit, but git_print_page_nav prints "log" and "shortlog" already with the same head. Noticed by Junio. Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 6e1496d6ac..8bf7bf4bb0 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2940,11 +2940,6 @@ sub git_commit { $cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)}, "blame"); } - if (defined $co{'parent'}) { - push @views_nav, - $cgi->a({-href => href(action=>"shortlog", hash=>$hash)}, "shortlog"), - $cgi->a({-href => href(action=>"log", hash=>$hash)}, "log"); - } git_header_html(undef, $expires); git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', $hash, $co{'tree'}, $hash, From db94b41aee0dfeae06e9c6f03d38a2aa30f8fd10 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 5 Oct 2006 13:30:31 -0700 Subject: [PATCH 026/114] gitweb: blame: Minimize vertical table row padding Minimize vertical table row padding for blame only. I discovered this while having the browser's blame output right next to my editor's window, only to notice how much vertically stretched the blame output was. Blame most likely shows source code and is in this way more "spartan" than the rest of the tables gitweb shows. This patch makes the blame table more vertically compact, thus being closer to what you'd see in your editor's window, as well as reusing more window estate to show more information (which in turn minimizes scrolling). Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index eb9fc3804b..668e69af1e 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -173,6 +173,12 @@ table.blame { border-collapse: collapse; } +table.blame td { + padding: 0px 5px; + font-size: 12px; + vertical-align: top; +} + th { padding: 2px 5px; font-size: 12px; From 51a7c66a73ce74fad3b7b05109ed6ce013202fa5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 23 Sep 2006 12:36:01 -0700 Subject: [PATCH 027/114] gitweb: Make the Git logo link target to point to the homepage It provides more useful information for causual Git users than the Git docs (especially about where to get Git and such). People can override with GITWEB_CONFIG if they want to. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano Acked-by: Petr Baudis --- gitweb/gitweb.perl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 8bf7bf4bb0..3320069171 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -51,6 +51,9 @@ our $logo = "++GITWEB_LOGO++"; # URI of GIT favicon, assumed to be image/png type our $favicon = "++GITWEB_FAVICON++"; +our $githelp_url = "http://git.or.cz/"; +our $githelp_label = "git homepage"; + # source of projects list our $projects_list = "++GITWEB_LIST++"; @@ -1373,7 +1376,9 @@ EOF print "\n" . "\n" . "
\n" . - "" . + "" . "\"git\"" . "\n"; print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; From d7014dc0811eb24f418830a30dacc203c1a580f4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 3 Oct 2006 23:09:56 +0200 Subject: [PATCH 028/114] Turn on recursive with --summary This makes "git log/diff --summary" imply recursive behaviour, whose effect is summarized in one test output: --- a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial +++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial @@ -5,7 +5,7 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial - create mode 040000 dir + create mode 100644 dir/sub create mode 100644 file0 create mode 100644 file2 $ When a file is created in a subdirectory, we used to say just the directory name only when that directory also was created, which did not make sense from two reasons. It is not any more significant to create a new file in a new directory than to create a new file in an existing directory, and even if it were, reportinging the new directory name without saying the actual filename is not useful. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 1 + t/t4013/diff.diff-tree_--pretty_--root_--summary_initial | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index fb8243261c..9dbbed75ba 100644 --- a/diff.c +++ b/diff.c @@ -1741,6 +1741,7 @@ int diff_setup_done(struct diff_options *options) */ if (options->output_format & (DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT | + DIFF_FORMAT_SUMMARY | DIFF_FORMAT_CHECKDIFF)) options->recursive = 1; /* diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial index ea48205537..58e5f74aea 100644 --- a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial +++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial @@ -5,7 +5,7 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial - create mode 040000 dir + create mode 100644 dir/sub create mode 100644 file0 create mode 100644 file2 $ From bc108f63dad7a5f6d95418cb78a587f5f570eae6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 16:36:15 -0700 Subject: [PATCH 029/114] git-send-email: avoid uninitialized variable warning. The code took length of $reply_to when it was not even defined, causing -w to warn. Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 4a20310841..3f50abaeb6 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -538,7 +538,7 @@ foreach my $t (@files) { send_message(); # set up for the next message - if ($chain_reply_to || length($reply_to) == 0) { + if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) { $reply_to = $message_id; if (length $references > 0) { $references .= " $message_id"; From abd6970acad5d758f48c13f7420367ae8216038e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 17:54:14 -0700 Subject: [PATCH 030/114] cherry-pick: make -r the default And introduce -x to expose (possibly) private commit object name for people who cherry-pick between public branches. Signed-off-by: Junio C Hamano --- Documentation/git-cherry-pick.txt | 23 ++++++++++++++++------- git-revert.sh | 12 +++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index bfa950ca19..875edb6b9f 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-r] +'git-cherry-pick' [--edit] [-n] [-x] DESCRIPTION ----------- @@ -24,13 +24,22 @@ OPTIONS With this option, `git-cherry-pick` will let you edit the commit message prior committing. --r|--replay:: - Usually the command appends which commit was +-x:: + Cause the command to append which commit was cherry-picked after the original commit message when - making a commit. This option, '--replay', causes it to - use the original commit message intact. This is useful - when you are reordering the patches in your private tree - before publishing. + making a commit. Do not use this option if you are + cherry-picking from your private branch because the + information is useless to the recipient. If on the + other hand you are cherry-picking between two publicly + visible branches (e.g. backporting a fix to a + maintenance branch for an older release from a + development branch), adding this information can be + useful. + +-r|--replay:: + It used to be that the command defaulted to do `-x` + described above, and `-r` was to disable it. Now the + default is not to do `-x` so this option is a no-op. -n|--no-commit:: Usually the command automatically creates a commit with diff --git a/git-revert.sh b/git-revert.sh index 2bf35d116c..0784f74c18 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -12,13 +12,13 @@ case "$0" in *-cherry-pick* ) edit= me=cherry-pick - USAGE='[--edit] [-n] [-r] ' ;; + USAGE='[--edit] [-n] [-r] [-x] ' ;; * ) die "What are you talking about?" ;; esac . git-sh-setup -no_commit= replay= +no_commit= replay=t while case "$#" in 0) break ;; esac do case "$1" in @@ -32,8 +32,10 @@ do --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) edit= ;; - -r|--r|--re|--rep|--repl|--repla|--replay) - replay=t + -r) + : no-op ;; + -x|--i-really-want-to-expose-my-private-commit-object-name) + replay= ;; -*) usage @@ -121,7 +123,7 @@ cherry-pick) git-cat-file commit $commit | sed -e '1,/^$/d' case "$replay" in '') - echo "(cherry picked from $commit commit)" + echo "(cherry picked from commit $commit)" test "$rev" = "$commit" || echo "(original 'git cherry-pick' arguments: $@)" ;; From ce91c2f6538fc4a905882f2a7a57d814d82b13e8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 16:36:49 -0700 Subject: [PATCH 031/114] git-send-email: do not drop custom headers the user prepared The command picked up only Subject, CC, and From headers in the incoming mbox text. Sending out patches prepared by git-format-patch with user's custom headers was impossible with that. Just keep the ones it does not need to look at and add them to the header of the message when sending it out. Signed-off-by: Junio C Hamano --- git-send-email.perl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 3f50abaeb6..2fd5e87934 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -83,7 +83,7 @@ sub cleanup_compose_files(); my $compose_filename = ".msg.$$"; # Variables we fill in automatically, or via prompting: -my (@to,@cc,@initial_cc,@bcclist, +my (@to,@cc,@initial_cc,@bcclist,@xh, $initial_reply_to,$initial_subject,@files,$from,$compose,$time); # Behavior modification variables @@ -422,6 +422,9 @@ X-Mailer: git-send-email $gitversion $header .= "In-Reply-To: $reply_to\n"; $header .= "References: $references\n"; } + if (@xh) { + $header .= join("\n", @xh) . "\n"; + } if ($smtp_server =~ m#^/#) { my $pid = open my $sm, '|-'; @@ -472,6 +475,7 @@ foreach my $t (@files) { my $author_not_sender = undef; @cc = @initial_cc; + @xh = (); my $found_mbox = 0; my $header_done = 0; $message = ""; @@ -495,6 +499,9 @@ foreach my $t (@files) { $2, $_) unless $quiet; push @cc, $2; } + elsif (/^[-A-Za-z]+:\s+\S/) { + push @xh, $_; + } } else { # In the traditional From 7a2a0d214169a5d6f22fc62c4ae6a7ca5b27fb3e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 23:40:15 -0700 Subject: [PATCH 032/114] git-send-email: real name with period need to be dq-quoted on From: line An author name like 'A. U. Thor " is not a valid RFC 2822 address; when placing it on From: line, we would need to quote it, like this: Signed-off-by: "Junio C. Hamano" --- git-send-email.perl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-send-email.perl b/git-send-email.perl index 2fd5e87934..21b3686b2c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -409,6 +409,11 @@ sub send_message $gitversion = Git::version(); } + my ($author_name) = ($from =~ /^(.*?)\s+ Date: Fri, 6 Oct 2006 11:15:03 +0200 Subject: [PATCH 033/114] Add default values for --window and --depth to the docs Currently, you actually have to read the source to find out the default values. While at it, fix two typos and suggest that these options actually take a parameter in git-pack-objects.txt. Signed-off-by: Dennis Stosberg Signed-off-by: Junio C Hamano --- Documentation/git-pack-objects.txt | 7 ++++--- Documentation/git-repack.txt | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index d4661ddc2f..f52e8fa8bf 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -71,11 +71,11 @@ base-name:: --all:: This implies `--revs`. In addition to the list of revision arguments read from the standard input, pretend - as if all refs under `$GIT_DIR/refs` are specifed to be + as if all refs under `$GIT_DIR/refs` are specified to be included. ---window and --depth:: - These two options affects how the objects contained in +--window=[N], --depth=[N]:: + These two options affect how the objects contained in the pack are stored using delta compression. The objects are first internally sorted by type, size and optionally names and compared against the other objects @@ -84,6 +84,7 @@ base-name:: it too deep affects the performance on the unpacker side, because delta data needs to be applied that many times to get to the necessary object. + The default value for both --window and --depth is 10. --incremental:: This flag causes an object already in a pack ignored diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 49f7e0a4a4..d2eaa0995d 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -57,13 +57,14 @@ OPTIONS `git update-server-info`. --window=[N], --depth=[N]:: - These two options affects how the objects contained in the pack are + These two options affect how the objects contained in the pack are stored using delta compression. The objects are first internally sorted by type, size and optionally names and compared against the other objects within `--window` to see if using delta compression saves space. `--depth` limits the maximum delta depth; making it too deep affects the performance on the unpacker side, because delta data needs to be applied that many times to get to the necessary object. + The default value for both --window and --depth is 10. Author From 847abc0f2a8d57bf4c1f13e948995491b6bc6f58 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Mon, 2 Oct 2006 06:21:33 +0900 Subject: [PATCH 034/114] gitweb: refactor decode() for utf8 conversion we already had a few place using decode() to convert perl internal encode to utf8. added a new thin wrapper to do just that. Signed-off-by: Yasushi SHOJI Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3320069171..3cc0e965fc 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -443,6 +443,12 @@ sub validate_refname { return $input; } +# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT); +sub to_utf8 { + my $str = shift; + return decode("utf8", $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 { @@ -465,7 +471,7 @@ sub esc_url { # replace invalid utf8 character with SUBSTITUTION sequence sub esc_html { my $str = shift; - $str = decode("utf8", $str, Encode::FB_DEFAULT); + $str = to_utf8($str); $str = escapeHTML($str); $str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file) $str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1) @@ -668,7 +674,7 @@ sub format_subject_html { if (length($short) < length($long)) { return $cgi->a({-href => $href, -class => "list subject", - -title => decode("utf8", $long, Encode::FB_DEFAULT)}, + -title => to_utf8($long)}, esc_html($short) . $extra); } else { return $cgi->a({-href => $href, -class => "list subject"}, @@ -845,7 +851,7 @@ sub git_get_projects_list { -e "$projectroot/$path/$export_ok")) { my $pr = { path => $path, - owner => decode("utf8", $owner, Encode::FB_DEFAULT), + owner => to_utf8($owner), }; push @list, $pr } @@ -874,7 +880,7 @@ sub git_get_project_owner { $pr = unescape($pr); $ow = unescape($ow); if ($pr eq $project) { - $owner = decode("utf8", $ow, Encode::FB_DEFAULT); + $owner = to_utf8($ow); last; } } @@ -1236,7 +1242,7 @@ sub get_file_owner { } my $owner = $gcos; $owner =~ s/[,;].*$//; - return decode("utf8", $owner, Encode::FB_DEFAULT); + return to_utf8($owner); } ## ...................................................................... @@ -3589,7 +3595,7 @@ XML "\n"; } print "
\n"; @@ -3598,7 +3604,7 @@ XML next; } my $file = esc_html(unquote($7)); - $file = decode("utf8", $file, Encode::FB_DEFAULT); + $file = to_utf8($file); print "$file
\n"; } print "]]>\n" . From 55ff35cb649e77f7633f9a30e9988ecad1371fe5 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Fri, 6 Oct 2006 15:57:52 +0200 Subject: [PATCH 035/114] Show snapshot link in shortlog only if have_snapsho Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3cc0e965fc..276a84244b 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2033,8 +2033,10 @@ sub git_shortlog_body { print "\n" . "" . $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " . - $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . " | " . - $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot"); + $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree"); + if (gitweb_have_snapshot()) { + print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot"); + } print "\n" . "\n"; } From 689b7f5ccbbb71f5a1a165cfb1e24abd42c7d009 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Fri, 6 Oct 2006 18:00:17 +0200 Subject: [PATCH 036/114] gitweb: Separate (new) and (deleted) in commitdiff by a space Currently it's pasted to the sha1 of the blob and looks ugly. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 276a84244b..3d677a9db5 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1922,14 +1922,14 @@ sub git_patchset_body { print "
" . file_type($diffinfo->{'to_mode'}) . ":" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash, hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'to_id'}) . "(new)" . + $diffinfo->{'to_id'}) . " (new)" . "
\n"; # class="diff_info" } elsif ($diffinfo->{'status'} eq "D") { # deleted print "
" . file_type($diffinfo->{'from_mode'}) . ":" . $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent, hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})}, - $diffinfo->{'from_id'}) . "(deleted)" . + $diffinfo->{'from_id'}) . " (deleted)" . "
\n"; # class="diff_info" } elsif ($diffinfo->{'status'} eq "R" || # renamed From 7e0fe5c939bdd5cc2885d21799e95304a46bf706 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Fri, 6 Oct 2006 18:55:04 +0200 Subject: [PATCH 037/114] gitweb: Handle commits with empty commit messages more reasonably Currently those look very weird, you can't get easily at the commit view etc. This patch makes their title '(no commit message)'. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3d677a9db5..c7a245ac2d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1071,6 +1071,9 @@ sub parse_commit { last; } } + if ($co{'title'} eq "") { + $co{'title'} = $co{'title_short'} = '(no commit message)'; + } # remove added spaces foreach my $line (@commit_lines) { $line =~ s/^ //; From a144154f85fe1d61da2a515e79ea1d08b5137f20 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Fri, 6 Oct 2006 18:59:33 +0200 Subject: [PATCH 038/114] gitweb: [commit view] Do not suppress commitdiff link in root commit There's no reason for that, the commitdiff view is meaningful for the root commit as well and we link to it everywhere else. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index c7a245ac2d..cdb09c433a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2957,7 +2957,7 @@ sub git_commit { "blame"); } git_header_html(undef, $expires); - git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff', + git_print_page_nav('commit', '', $hash, $co{'tree'}, $hash, join (' | ', @views_nav)); From 3de63c3f9f82cd0a43f3f69dd1e3662bc101d629 Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Sat, 7 Oct 2006 21:07:40 +0200 Subject: [PATCH 039/114] git-commit: fix coding style. git-commit.sh was using a mixture of spaces and tabs for indentation. This is changed to one tab per indentation level. No code changes. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- git-commit.sh | 458 +++++++++++++++++++++++++------------------------- 1 file changed, 230 insertions(+), 228 deletions(-) diff --git a/git-commit.sh b/git-commit.sh index 6f6cbda898..5b1cf85825 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -41,21 +41,21 @@ run_status () { # so the regular index file is what we use to compare. if test '' != "$TMP_INDEX" then - GIT_INDEX_FILE="$TMP_INDEX" - export GIT_INDEX_FILE + GIT_INDEX_FILE="$TMP_INDEX" + export GIT_INDEX_FILE elif test -f "$NEXT_INDEX" then - GIT_INDEX_FILE="$NEXT_INDEX" - export GIT_INDEX_FILE + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE fi - case "$status_only" in - t) color= ;; - *) color=--nocolor ;; - esac - git-runstatus ${color} \ - ${verbose:+--verbose} \ - ${amend:+--amend} \ + case "$status_only" in + t) color= ;; + *) color=--nocolor ;; + esac + git-runstatus ${color} \ + ${verbose:+--verbose} \ + ${amend:+--amend} \ ${untracked_files:+--untracked} } @@ -87,179 +87,181 @@ only_include_assumed= untracked_files= while case "$#" in 0) break;; esac do - case "$1" in - -F|--F|-f|--f|--fi|--fil|--file) - case "$#" in 1) usage ;; esac - shift - no_edit=t - log_given=t$log_given - logfile="$1" - shift - ;; - -F*|-f*) - no_edit=t - log_given=t$log_given - logfile=`expr "z$1" : 'z-[Ff]\(.*\)'` - shift - ;; - --F=*|--f=*|--fi=*|--fil=*|--file=*) - no_edit=t - log_given=t$log_given - logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'` - shift - ;; - -a|--a|--al|--all) - all=t - shift - ;; - --au=*|--aut=*|--auth=*|--autho=*|--author=*) - force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'` - shift - ;; - --au|--aut|--auth|--autho|--author) - case "$#" in 1) usage ;; esac - shift - force_author="$1" - shift - ;; - -e|--e|--ed|--edi|--edit) - edit_flag=t - shift - ;; - -i|--i|--in|--inc|--incl|--inclu|--includ|--include) - also=t - shift - ;; - -o|--o|--on|--onl|--only) - only=t - shift - ;; - -m|--m|--me|--mes|--mess|--messa|--messag|--message) - case "$#" in 1) usage ;; esac - shift - log_given=m$log_given - if test "$log_message" = '' - then - log_message="$1" - else - log_message="$log_message + case "$1" in + -F|--F|-f|--f|--fi|--fil|--file) + case "$#" in 1) usage ;; esac + shift + no_edit=t + log_given=t$log_given + logfile="$1" + shift + ;; + -F*|-f*) + no_edit=t + log_given=t$log_given + logfile=`expr "z$1" : 'z-[Ff]\(.*\)'` + shift + ;; + --F=*|--f=*|--fi=*|--fil=*|--file=*) + no_edit=t + log_given=t$log_given + logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'` + shift + ;; + -a|--a|--al|--all) + all=t + shift + ;; + --au=*|--aut=*|--auth=*|--autho=*|--author=*) + force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'` + shift + ;; + --au|--aut|--auth|--autho|--author) + case "$#" in 1) usage ;; esac + shift + force_author="$1" + shift + ;; + -e|--e|--ed|--edi|--edit) + edit_flag=t + shift + ;; + -i|--i|--in|--inc|--incl|--inclu|--includ|--include) + also=t + shift + ;; + -o|--o|--on|--onl|--only) + only=t + shift + ;; + -m|--m|--me|--mes|--mess|--messa|--messag|--message) + case "$#" in 1) usage ;; esac + shift + log_given=m$log_given + if test "$log_message" = '' + then + log_message="$1" + else + log_message="$log_message $1" - fi - no_edit=t - shift - ;; - -m*) - log_given=m$log_given - if test "$log_message" = '' - then - log_message=`expr "z$1" : 'z-m\(.*\)'` - else - log_message="$log_message + fi + no_edit=t + shift + ;; + -m*) + log_given=m$log_given + if test "$log_message" = '' + then + log_message=`expr "z$1" : 'z-m\(.*\)'` + else + log_message="$log_message `expr "z$1" : 'z-m\(.*\)'`" - fi - no_edit=t - shift - ;; - --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) - log_given=m$log_given - if test "$log_message" = '' - then - log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'` - else - log_message="$log_message + fi + no_edit=t + shift + ;; + --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) + log_given=m$log_given + if test "$log_message" = '' + then + log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'` + else + log_message="$log_message `expr "z$1" : 'zq-[^=]*=\(.*\)'`" - fi - no_edit=t - shift - ;; - -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify) - verify= - shift - ;; - --a|--am|--ame|--amen|--amend) - amend=t - log_given=t$log_given - use_commit=HEAD - shift - ;; - -c) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit= - shift - ;; - --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\ - --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\ - --reedit-messag=*|--reedit-message=*) - log_given=t$log_given - use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'` - no_edit= - shift - ;; - --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\ - --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|--reedit-message) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit= - shift - ;; - -C) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit=t - shift - ;; - --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\ - --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\ - --reuse-message=*) - log_given=t$log_given - use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'` - no_edit=t - shift - ;; - --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\ - --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message) - case "$#" in 1) usage ;; esac - shift - log_given=t$log_given - use_commit="$1" - no_edit=t - shift - ;; - -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) - signoff=t - shift - ;; - -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) - verbose=t - shift - ;; - -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\ - --untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\ - --untracked-files) - untracked_files=t - shift - ;; - --) - shift - break - ;; - -*) - usage - ;; - *) - break - ;; - esac + fi + no_edit=t + shift + ;; + -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\ + --no-verify) + verify= + shift + ;; + --a|--am|--ame|--amen|--amend) + amend=t + log_given=t$log_given + use_commit=HEAD + shift + ;; + -c) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + shift + ;; + --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\ + --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\ + --reedit-messag=*|--reedit-message=*) + log_given=t$log_given + use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'` + no_edit= + shift + ;; + --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\ + --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\ + --reedit-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + shift + ;; + -C) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + shift + ;; + --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\ + --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\ + --reuse-message=*) + log_given=t$log_given + use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'` + no_edit=t + shift + ;; + --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\ + --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + shift + ;; + -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) + signoff=t + shift + ;; + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t + shift + ;; + -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\ + --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\ + --untracked-file|--untracked-files) + untracked_files=t + shift + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac done case "$edit_flag" in t) no_edit= ;; esac @@ -268,33 +270,33 @@ case "$edit_flag" in t) no_edit= ;; esac case "$amend,$initial_commit" in t,t) - die "You do not have anything to amend." ;; + die "You do not have anything to amend." ;; t,) - if [ -f "$GIT_DIR/MERGE_HEAD" ]; then - die "You are in the middle of a merge -- cannot amend." - fi ;; + if [ -f "$GIT_DIR/MERGE_HEAD" ]; then + die "You are in the middle of a merge -- cannot amend." + fi ;; esac case "$log_given" in tt*) - die "Only one of -c/-C/-F can be used." ;; + die "Only one of -c/-C/-F can be used." ;; *tm*|*mt*) - die "Option -m cannot be combined with -c/-C/-F." ;; + die "Option -m cannot be combined with -c/-C/-F." ;; esac case "$#,$also,$only,$amend" in *,t,t,*) - die "Only one of --include/--only can be used." ;; + die "Only one of --include/--only can be used." ;; 0,t,,* | 0,,t,) - die "No paths with --include/--only does not make sense." ;; + die "No paths with --include/--only does not make sense." ;; 0,,t,t) - only_include_assumed="# Clever... amending the last one with dirty index." ;; + only_include_assumed="# Clever... amending the last one with dirty index." ;; 0,,,*) - ;; + ;; *,,,*) - only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..." - also= - ;; + only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..." + also= + ;; esac unset only case "$all,$also,$#" in @@ -341,47 +343,47 @@ t,) ,) case "$#" in 0) - ;; # commit as-is + ;; # commit as-is *) - if test -f "$GIT_DIR/MERGE_HEAD" - then - refuse_partial "Cannot do a partial commit during a merge." - fi - TMP_INDEX="$GIT_DIR/tmp-index$$" - if test -z "$initial_commit" - then - # make sure index is clean at the specified paths, or - # they are additions. - dirty_in_index=`git-diff-index --cached --name-status \ - --diff-filter=DMTU HEAD -- "$@"` - test -z "$dirty_in_index" || - refuse_partial "Different in index and the last commit: + if test -f "$GIT_DIR/MERGE_HEAD" + then + refuse_partial "Cannot do a partial commit during a merge." + fi + TMP_INDEX="$GIT_DIR/tmp-index$$" + if test -z "$initial_commit" + then + # make sure index is clean at the specified paths, or + # they are additions. + dirty_in_index=`git-diff-index --cached --name-status \ + --diff-filter=DMTU HEAD -- "$@"` + test -z "$dirty_in_index" || + refuse_partial "Different in index and the last commit: $dirty_in_index" - fi - commit_only=`git-ls-files --error-unmatch -- "$@"` || exit + fi + commit_only=`git-ls-files --error-unmatch -- "$@"` || exit - # Build the temporary index and update the real index - # the same way. - if test -z "$initial_commit" - then - cp "$THIS_INDEX" "$TMP_INDEX" - GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD - else - rm -f "$TMP_INDEX" - fi || exit + # Build the temporary index and update the real index + # the same way. + if test -z "$initial_commit" + then + cp "$THIS_INDEX" "$TMP_INDEX" + GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD + else + rm -f "$TMP_INDEX" + fi || exit - echo "$commit_only" | - GIT_INDEX_FILE="$TMP_INDEX" \ - git-update-index --add --remove --stdin && + echo "$commit_only" | + GIT_INDEX_FILE="$TMP_INDEX" \ + git-update-index --add --remove --stdin && - save_index && - echo "$commit_only" | - ( - GIT_INDEX_FILE="$NEXT_INDEX" - export GIT_INDEX_FILE - git-update-index --remove --stdin - ) || exit - ;; + save_index && + echo "$commit_only" | + ( + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + git-update-index --remove --stdin + ) || exit + ;; esac ;; esac @@ -399,7 +401,7 @@ else fi GIT_INDEX_FILE="$USE_INDEX" \ - git-update-index -q $unmerged_ok_if_status --refresh || exit + git-update-index -q $unmerged_ok_if_status --refresh || exit ################################################################ # If the request is status, just show it and exit. From 7a0cf2d0138cf3abd3f2c3c9a1aa4dc55bf0700f Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Sat, 7 Oct 2006 21:27:46 +0200 Subject: [PATCH 040/114] test-lib: separate individual test better in verbose mode. When running tests with --verbose it is difficult to see where one test starts and where it ends because everything is printed in one big lump. Fix that by printing one single newline between each test. Signed-off-by: Martin Waitz Signed-off-by: Junio C Hamano --- t/test-lib.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/t/test-lib.sh b/t/test-lib.sh index b523fef339..2488e6eae1 100755 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -135,6 +135,7 @@ test_expect_failure () { else test_failure_ "$@" fi + echo >&3 "" } test_expect_success () { @@ -148,6 +149,7 @@ test_expect_success () { else test_failure_ "$@" fi + echo >&3 "" } test_expect_code () { @@ -161,6 +163,7 @@ test_expect_code () { else test_failure_ "$@" fi + echo >&3 "" } # Most tests can use the created repository, but some amy need to create more. From 45a3b12cfd3eaa05bbb0954790d5be5b8240a7b5 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Sat, 7 Oct 2006 15:17:47 +0200 Subject: [PATCH 041/114] gitweb: Document features better This expands gitweb/README to talk some more about GITWEB_CONFIG, moves feature-specific documentation in gitweb.cgi to the inside of the %features array, and adds some short description of all the features. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/README | 16 ++++++++++++- gitweb/gitweb.perl | 59 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/gitweb/README b/gitweb/README index 27c6dac143..abbaf6b8bf 100644 --- a/gitweb/README +++ b/gitweb/README @@ -24,11 +24,25 @@ You can specify the following configuration variables when building GIT: * GITWEB_LOGO Points to the location where you put git-logo.png on your web server. * GITWEB_CONFIG - This file will be loaded using 'require'. If the environment + This file will be loaded using 'require' and can be used to override any + of the options above as well as some other options - see the top of + 'gitweb.cgi' for their full list and description. If the environment $GITWEB_CONFIG is set when gitweb.cgi is executed the file in the environment variable will be loaded instead of the file specified when gitweb.cgi was created. +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). +See the top of 'gitweb.cgi' for the list of variables and some description. +The most notable thing that is not configurable at compile time are the +optional features, stored in the '%features' variable. You can find further +description on how to reconfigure the default features setting in your +`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'. + + Originally written by: Kay Sievers diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0ff6f7c1e4..1d5217e334 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -87,22 +87,63 @@ our %feature = ( # # use gitweb_check_feature() to check if is enabled + # Enable the 'blame' blob view, showing the last commit that modified + # each line in the file. This can be very CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'blame'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.blame = 0|1; 'blame' => { 'sub' => \&feature_blame, 'override' => 0, 'default' => [0]}, + # Enable the 'snapshot' link, providing a compressed tarball of any + # tree. This can potentially generate high traffic if you have large + # project. + + # To disable system wide have in $GITWEB_CONFIG + # $feature{'snapshot'}{'default'} = [undef]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'blame'}{'override'} = 1; + # and in project config gitweb.snapshot = none|gzip|bzip2; 'snapshot' => { 'sub' => \&feature_snapshot, 'override' => 0, # => [content-encoding, suffix, program] 'default' => ['x-gzip', 'gz', 'gzip']}, + # Enable the pickaxe search, which will list the commits that modified + # a given string in a file. This can be practical and quite faster + # alternative to 'blame', but still potentially CPU-intensive. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pickaxe'}{'default'} = [1]; + # To have project specific config enable override in $GITWEB_CONFIG + # $feature{'pickaxe'}{'override'} = 1; + # and in project config gitweb.pickaxe = 0|1; 'pickaxe' => { 'sub' => \&feature_pickaxe, 'override' => 0, 'default' => [1]}, + # Make gitweb use an alternative format of the URLs which can be + # more readable and natural-looking: project name is embedded + # directly in the path and the query string contains other + # auxiliary information. All gitweb installations recognize + # URL in either format; this configures in which formats gitweb + # generates links. + + # To enable system wide have in $GITWEB_CONFIG + # $feature{'pathinfo'}{'default'} = [1]; + # Project specific override is not supported. + + # Note that you will need to change the default location of CSS, + # favicon, logo and possibly other files to an absolute URL. Also, + # if gitweb.cgi serves as your indexfile, you will need to force + # $my_uri to contain the script name in your $GITWEB_CONFIG. 'pathinfo' => { 'override' => 0, 'default' => [0]}, @@ -123,12 +164,6 @@ sub gitweb_check_feature { return $sub->(@defaults); } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'blame'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.blame = 0|1; - sub feature_blame { my ($val) = git_get_project_config('blame', '--bool'); @@ -141,12 +176,6 @@ sub feature_blame { return $_[0]; } -# To disable system wide have in $GITWEB_CONFIG -# $feature{'snapshot'}{'default'} = [undef]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'blame'}{'override'} = 1; -# and in project config gitweb.snapshot = none|gzip|bzip2 - sub feature_snapshot { my ($ctype, $suffix, $command) = @_; @@ -170,12 +199,6 @@ sub gitweb_have_snapshot { return $have_snapshot; } -# To enable system wide have in $GITWEB_CONFIG -# $feature{'pickaxe'}{'default'} = [1]; -# To have project specific config enable override in $GITWEB_CONFIG -# $feature{'pickaxe'}{'override'} = 1; -# and in project config gitweb.pickaxe = 0|1; - sub feature_pickaxe { my ($val) = git_get_project_config('pickaxe', '--bool'); From cf72fb07b77c73b4777b6a2e0836e3029c5f0f3c Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 7 Oct 2006 01:47:24 +0200 Subject: [PATCH 042/114] git-archive --format=zip: use default version ID Use 10 for the "version needed to extract" field. This is the default value, and we want to use it because we don't do anything special. Info-ZIP's zip uses it, too. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index 3ffdad68d1..ae7462353d 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -214,7 +214,7 @@ static int write_zip_entry(const unsigned char *sha1, copy_le32(dirent.magic, 0x02014b50); copy_le16(dirent.creator_version, 0); - copy_le16(dirent.version, 20); + copy_le16(dirent.version, 10); copy_le16(dirent.flags, 0); copy_le16(dirent.compression_method, method); copy_le16(dirent.mtime, zip_time); @@ -236,7 +236,7 @@ static int write_zip_entry(const unsigned char *sha1, zip_dir_entries++; copy_le32(header.magic, 0x04034b50); - copy_le16(header.version, 20); + copy_le16(header.version, 10); copy_le16(header.flags, 0); copy_le16(header.compression_method, method); copy_le16(header.mtime, zip_time); From 62cdce17c57a28240048c5064fab57edae19657f Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 7 Oct 2006 01:47:35 +0200 Subject: [PATCH 043/114] git-archive --format=zip: add symlink support Add symlink support to ZIP file creation, and a few tests. This implementation sets the "version made by" field (creator_version) to Unix for symlinks, only; regular files and directories are still marked as originating from FAT/VFAT/NTFS. Also set "external file attributes" (attr2) to 0 for regular files and 16 for directories (FAT attribute), and to the file mode for symlinks. We could always set the creator_version to Unix and include the mode, but then Info-ZIP unzip would set the mode of the extracted files to *exactly* the value stored in attr2. The FAT trick makes it apply the umask instead. Note: FAT has no executable bit, so this information is not stored in the ZIP file. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- archive-zip.c | 13 +++++++++---- t/t5000-tar-tree.sh | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/archive-zip.c b/archive-zip.c index ae7462353d..28e7352e98 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -145,6 +145,7 @@ static int write_zip_entry(const unsigned char *sha1, { struct zip_local_header header; struct zip_dir_header dirent; + unsigned long attr2; unsigned long compressed_size; unsigned long uncompressed_size; unsigned long crc; @@ -172,12 +173,16 @@ static int write_zip_entry(const unsigned char *sha1, if (S_ISDIR(mode)) { method = 0; + attr2 = 16; result = READ_TREE_RECURSIVE; out = NULL; uncompressed_size = 0; compressed_size = 0; - } else if (S_ISREG(mode)) { - method = zlib_compression_level == 0 ? 0 : 8; + } else if (S_ISREG(mode) || S_ISLNK(mode)) { + method = 0; + attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0; + if (S_ISREG(mode) && zlib_compression_level != 0) + method = 8; result = 0; buffer = read_sha1_file(sha1, type, &size); if (!buffer) @@ -213,7 +218,7 @@ static int write_zip_entry(const unsigned char *sha1, } copy_le32(dirent.magic, 0x02014b50); - copy_le16(dirent.creator_version, 0); + copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0); copy_le16(dirent.version, 10); copy_le16(dirent.flags, 0); copy_le16(dirent.compression_method, method); @@ -227,7 +232,7 @@ static int write_zip_entry(const unsigned char *sha1, copy_le16(dirent.comment_length, 0); copy_le16(dirent.disk, 0); copy_le16(dirent.attr1, 0); - copy_le32(dirent.attr2, 0); + copy_le32(dirent.attr2, attr2); copy_le32(dirent.offset, zip_offset); memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header)); zip_dir_offset += sizeof(struct zip_dir_header); diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 278eb66701..cf08e9279c 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -26,6 +26,7 @@ commit id embedding: . ./test-lib.sh TAR=${TAR:-tar} +UNZIP=${UNZIP:-unzip} test_expect_success \ 'populate workdir' \ @@ -95,4 +96,38 @@ test_expect_success \ 'validate file contents with prefix' \ 'diff -r a c/prefix/a' +test_expect_success \ + 'git-archive --format=zip' \ + 'git-archive --format=zip HEAD >d.zip' + +test_expect_success \ + 'extract ZIP archive' \ + '(mkdir d && cd d && $UNZIP ../d.zip)' + +test_expect_success \ + 'validate filenames' \ + '(cd d/a && find .) | sort >d.lst && + diff a.lst d.lst' + +test_expect_success \ + 'validate file contents' \ + 'diff -r a d/a' + +test_expect_success \ + 'git-archive --format=zip with prefix' \ + 'git-archive --format=zip --prefix=prefix/ HEAD >e.zip' + +test_expect_success \ + 'extract ZIP archive with prefix' \ + '(mkdir e && cd e && $UNZIP ../e.zip)' + +test_expect_success \ + 'validate filenames with prefix' \ + '(cd e/prefix/a && find .) | sort >e.lst && + diff a.lst e.lst' + +test_expect_success \ + 'validate file contents with prefix' \ + 'diff -r a e/prefix/a' + test_done From e6b0964af595bbc4dab2a63c17bc4c8064a28073 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 Oct 2006 03:09:05 -0700 Subject: [PATCH 044/114] Make git-send-email detect mbox-style patches more readily Earlier we insisted that mbox file to begin with "From ". That is fine as long as you feed format-patch output, but if you handcraft the input file, this is unnecessary burden. We should detect lines that look like e-mail headers and say that is also a mbox file. The other input file format is traditional "send lots of email", whose first line would never look like e-mail headers, so this is a safe change. The original patch was done by Matthew Wilcox, which checked explicitly for headers the script pays attention to. Signed-off-by: Junio C Hamano --- git-send-email.perl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 21b3686b2c..eb91270898 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -481,15 +481,21 @@ foreach my $t (@files) { my $author_not_sender = undef; @cc = @initial_cc; @xh = (); - my $found_mbox = 0; + my $input_format = undef; my $header_done = 0; $message = ""; while() { if (!$header_done) { - $found_mbox = 1, next if (/^From /); + if (/^From /) { + $input_format = 'mbox'; + next; + } chomp; + if (!defined $input_format && /^[-A-Za-z]+:\s/) { + $input_format = 'mbox'; + } - if ($found_mbox) { + if (defined $input_format && $input_format eq 'mbox') { if (/^Subject:\s+(.*)$/) { $subject = $1; @@ -514,6 +520,7 @@ foreach my $t (@files) { # line 1 = cc # line 2 = subject # So let's support that, too. + $input_format = 'lots'; if (@cc == 0) { printf("(non-mbox) Adding cc: %s from line '%s'\n", $_, $_) unless $quiet; From 9cb90b80fc1ec09d8e51451b18a7c8ef7eac8908 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 8 Oct 2006 15:44:50 +0200 Subject: [PATCH 045/114] git-tar-tree: don't RUN_SETUP Noted by Jiri Slaby, git-tar-tree --remote doesn't need to be run from inside of a git archive. Since git-tar-tree is now only a wrapper for git-archive, which calls setup_git_directory() as needed, we should drop the flag RUN_SETUP. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index b8e8622afb..d7103a4086 100644 --- a/git.c +++ b/git.c @@ -258,7 +258,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, - { "tar-tree", cmd_tar_tree, RUN_SETUP }, + { "tar-tree", cmd_tar_tree }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP }, { "update-ref", cmd_update_ref, RUN_SETUP }, From 9a7a62ff71c436fb5e024ec409ec28fca460a168 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 6 Oct 2006 12:31:05 +0200 Subject: [PATCH 046/114] gitweb: Cleanup Git logo and Git logo target generation Rename $githelp_url and $githelp_label to $logo_url and $logo_label to be more obvious what they refer to; while at it add commented out previous contents (git documentation at kernel.org). Add comment about logo size. Use $cgi->a(...) to generate Git logo link; it automatically escapes attribute values when it is needed. Escape href attribute using esc_url instead of (incorrect!) esc_html. Move styling of git logo element from "style" attribute to CSS via setting class to "logo". Perhaps we should set it by id rather than by class. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.css | 5 +++++ gitweb/gitweb.perl | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 668e69af1e..3f62b6d752 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -16,6 +16,11 @@ a:hover, a:visited, a:active { color: #880000; } +img.logo { + float: right; + border-width: 0px; +} + div.page_header { height: 25px; padding: 8px; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index cdb09c433a..0ec1eeffa1 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -46,13 +46,16 @@ our $home_text = "++GITWEB_HOMETEXT++"; # URI of default stylesheet our $stylesheet = "++GITWEB_CSS++"; -# URI of GIT logo +# URI of GIT logo (72x27 size) our $logo = "++GITWEB_LOGO++"; # URI of GIT favicon, assumed to be image/png type our $favicon = "++GITWEB_FAVICON++"; -our $githelp_url = "http://git.or.cz/"; -our $githelp_label = "git homepage"; +# URI and label (title) of GIT logo link +#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/"; +#our $logo_label = "git documentation"; +our $logo_url = "http://git.or.cz/"; +our $logo_label = "git homepage"; # source of projects list our $projects_list = "++GITWEB_LIST++"; @@ -1385,11 +1388,9 @@ EOF print "\n" . "\n" . "
\n" . - "" . - "\"git\"" . - "\n"; + $cgi->a({-href => esc_url($logo_url), + -title => $logo_label}, + qq()); print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / "; if (defined $project) { print $cgi->a({-href => href(action=>"summary")}, esc_html($project)); From adc446fe5d5f6dc1fb5edeaa9aa016ef94e70da1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Oct 2006 12:56:19 -0700 Subject: [PATCH 047/114] Add WEBDAV timeout to http-fetch. Sean writes: > On Sat, 07 Oct 2006 21:52:02 -0700 > Junio C Hamano wrote: > >> Using DAV, if it works with the server, has the advantage of not >> having to keep objects/info/packs up-to-date from repository >> owner's point of view. But the repository owner ends up keeping >> up-to-date as a side effect of keeping info/refs up-to-date >> anyway (as I do not see a code to read that information over >> DAV), so there is no point doing this over DAV in practice. >> >> Perhaps we should remove call to remote_ls() from >> fetch_indices() unconditionally, not just protected with >> NO_EXPAT and be done with it? > > That makes a lot of sense. A server really has to always provide > a objects/info/packs anyway, just to be fetchable today by clients > that are compiled with NO_EXPAT. And even for an isolated group where everybody knows that everybody else runs DAV-enabled clients, they need info/refs prepared for ls-remote and git-fetch script, which means you will run update-server-info to keep objects/info/packs up to date. Nick, do you see holes in my logic? -- >8 -- http-fetch.c: drop remote_ls() While doing remote_ls() over DAV potentially allows the server side not to keep objects/info/pack up-to-date, misconfigured or buggy servers can silently ignore or not to respond to DAV requests and makes the client hang. The server side (unfortunately) needs to run git-update-server-info even if remote_ls() removes the need to keep objects/info/pack file up-to-date, because the caller of git-http-fetch (git-fetch) and other clients that interact with the repository (e.g. git-ls-remote) need to read from info/refs file (there is no code to make that unnecessary by using DAV yet). Perhaps the right solution in the longer-term is to make info/refs also unnecessary by using DAV, and we would want to resurrect the code this patch removes when we do so, but let's drop remote_ls() implementation for now. It is causing problems without really helping anything yet. git will keep it for us until we need it next time. Signed-off-by: Junio C Hamano --- http-fetch.c | 257 --------------------------------------------------- 1 file changed, 257 deletions(-) diff --git a/http-fetch.c b/http-fetch.c index bc74f30f76..396552da02 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -4,35 +4,6 @@ #include "fetch.h" #include "http.h" -#ifndef NO_EXPAT -#include - -/* Definitions for DAV requests */ -#define DAV_PROPFIND "PROPFIND" -#define DAV_PROPFIND_RESP ".multistatus.response" -#define DAV_PROPFIND_NAME ".multistatus.response.href" -#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection" -#define PROPFIND_ALL_REQUEST "\n\n\n" - -/* Definitions for processing XML DAV responses */ -#ifndef XML_STATUS_OK -enum XML_Status { - XML_STATUS_OK = 1, - XML_STATUS_ERROR = 0 -}; -#define XML_STATUS_OK 1 -#define XML_STATUS_ERROR 0 -#endif - -/* Flags that control remote_ls processing */ -#define PROCESS_FILES (1u << 0) -#define PROCESS_DIRS (1u << 1) -#define RECURSIVE (1u << 2) - -/* Flags that remote_ls passes to callback functions */ -#define IS_DIR (1u << 0) -#endif - #define PREV_BUF_SIZE 4096 #define RANGE_HEADER_SIZE 30 @@ -90,30 +61,6 @@ struct alternates_request { int http_specific; }; -#ifndef NO_EXPAT -struct xml_ctx -{ - char *name; - int len; - char *cdata; - void (*userFunc)(struct xml_ctx *ctx, int tag_closed); - void *userData; -}; - -struct remote_ls_ctx -{ - struct alt_base *repo; - char *path; - void (*userFunc)(struct remote_ls_ctx *ls); - void *userData; - int flags; - char *dentry_name; - int dentry_flags; - int rc; - struct remote_ls_ctx *parent; -}; -#endif - static struct object_request *object_queue_head; static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, @@ -714,204 +661,6 @@ static void fetch_alternates(const char *base) free(url); } -#ifndef NO_EXPAT -static void -xml_start_tag(void *userData, const char *name, const char **atts) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - const char *c = strchr(name, ':'); - int new_len; - - if (c == NULL) - c = name; - else - c++; - - new_len = strlen(ctx->name) + strlen(c) + 2; - - if (new_len > ctx->len) { - ctx->name = xrealloc(ctx->name, new_len); - ctx->len = new_len; - } - strcat(ctx->name, "."); - strcat(ctx->name, c); - - free(ctx->cdata); - ctx->cdata = NULL; - - ctx->userFunc(ctx, 0); -} - -static void -xml_end_tag(void *userData, const char *name) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - const char *c = strchr(name, ':'); - char *ep; - - ctx->userFunc(ctx, 1); - - if (c == NULL) - c = name; - else - c++; - - ep = ctx->name + strlen(ctx->name) - strlen(c) - 1; - *ep = 0; -} - -static void -xml_cdata(void *userData, const XML_Char *s, int len) -{ - struct xml_ctx *ctx = (struct xml_ctx *)userData; - free(ctx->cdata); - ctx->cdata = xmalloc(len + 1); - strlcpy(ctx->cdata, s, len + 1); -} - -static int remote_ls(struct alt_base *repo, const char *path, int flags, - void (*userFunc)(struct remote_ls_ctx *ls), - void *userData); - -static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed) -{ - struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData; - - if (tag_closed) { - if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) { - if (ls->dentry_flags & IS_DIR) { - if (ls->flags & PROCESS_DIRS) { - ls->userFunc(ls); - } - if (strcmp(ls->dentry_name, ls->path) && - ls->flags & RECURSIVE) { - ls->rc = remote_ls(ls->repo, - ls->dentry_name, - ls->flags, - ls->userFunc, - ls->userData); - } - } else if (ls->flags & PROCESS_FILES) { - ls->userFunc(ls); - } - } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) { - ls->dentry_name = xmalloc(strlen(ctx->cdata) - - ls->repo->path_len + 1); - strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len); - } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) { - ls->dentry_flags |= IS_DIR; - } - } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) { - free(ls->dentry_name); - ls->dentry_name = NULL; - ls->dentry_flags = 0; - } -} - -static int remote_ls(struct alt_base *repo, const char *path, int flags, - void (*userFunc)(struct remote_ls_ctx *ls), - void *userData) -{ - char *url = xmalloc(strlen(repo->base) + strlen(path) + 1); - struct active_request_slot *slot; - struct slot_results results; - struct buffer in_buffer; - struct buffer out_buffer; - char *in_data; - char *out_data; - XML_Parser parser = XML_ParserCreate(NULL); - enum XML_Status result; - struct curl_slist *dav_headers = NULL; - struct xml_ctx ctx; - struct remote_ls_ctx ls; - - ls.flags = flags; - ls.repo = repo; - ls.path = xstrdup(path); - ls.dentry_name = NULL; - ls.dentry_flags = 0; - ls.userData = userData; - ls.userFunc = userFunc; - ls.rc = 0; - - sprintf(url, "%s%s", repo->base, path); - - out_buffer.size = strlen(PROPFIND_ALL_REQUEST); - out_data = xmalloc(out_buffer.size + 1); - snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST); - out_buffer.posn = 0; - out_buffer.buffer = out_data; - - in_buffer.size = 4096; - in_data = xmalloc(in_buffer.size); - in_buffer.posn = 0; - in_buffer.buffer = in_data; - - dav_headers = curl_slist_append(dav_headers, "Depth: 1"); - dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); - - slot = get_active_slot(); - slot->results = &results; - curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer); - curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); - curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); - curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); - curl_easy_setopt(slot->curl, CURLOPT_URL, url); - curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); - curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); - - if (start_active_slot(slot)) { - run_active_slot(slot); - if (results.curl_result == CURLE_OK) { - ctx.name = xcalloc(10, 1); - ctx.len = 0; - ctx.cdata = NULL; - ctx.userFunc = handle_remote_ls_ctx; - ctx.userData = &ls; - XML_SetUserData(parser, &ctx); - XML_SetElementHandler(parser, xml_start_tag, - xml_end_tag); - XML_SetCharacterDataHandler(parser, xml_cdata); - result = XML_Parse(parser, in_buffer.buffer, - in_buffer.posn, 1); - free(ctx.name); - - if (result != XML_STATUS_OK) { - ls.rc = error("XML error: %s", - XML_ErrorString( - XML_GetErrorCode(parser))); - } - } else { - ls.rc = -1; - } - } else { - ls.rc = error("Unable to start PROPFIND request"); - } - - free(ls.path); - free(url); - free(out_data); - free(in_buffer.buffer); - curl_slist_free_all(dav_headers); - - return ls.rc; -} - -static void process_ls_pack(struct remote_ls_ctx *ls) -{ - unsigned char sha1[20]; - - if (strlen(ls->dentry_name) == 63 && - !strncmp(ls->dentry_name, "objects/pack/pack-", 18) && - has_extension(ls->dentry_name, ".pack")) { - get_sha1_hex(ls->dentry_name + 18, sha1); - setup_index(ls->repo, sha1); - } -} -#endif - static int fetch_indices(struct alt_base *repo) { unsigned char sha1[20]; @@ -934,12 +683,6 @@ static int fetch_indices(struct alt_base *repo) if (get_verbosely) fprintf(stderr, "Getting pack list for %s\n", repo->base); -#ifndef NO_EXPAT - if (remote_ls(repo, "objects/pack/", PROCESS_FILES, - process_ls_pack, NULL) == 0) - return 0; -#endif - url = xmalloc(strlen(repo->base) + 21); sprintf(url, "%s/objects/info/packs", repo->base); From 83936a29e275bc0c04f60d3333e4951a9e16b1fc Mon Sep 17 00:00:00 2001 From: Sasha Khapyorsky Date: Sun, 8 Oct 2006 23:31:18 +0200 Subject: [PATCH 048/114] git-svnimport.perl: copying directory from original SVN place When copying whole directory, if source directory is not in already imported tree, try to get it from original SVN location. This happens when source directory is not matched by provided 'trunk' and/or 'tags/branches' templates or when it is not part of specified SVN sub-project. Signed-off-by: Sasha Khapyorsky Signed-off-by: Junio C Hamano --- git-svnimport.perl | 93 +++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/git-svnimport.perl b/git-svnimport.perl index 988514e293..4ae0eecd37 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -193,6 +193,13 @@ sub ignore { } } +sub dir_list { + my($self,$path,$rev) = @_; + my ($dirents,undef,$properties) + = $self->{'svn'}->get_dir($path,$rev,undef); + return $dirents; +} + package main; use URI; @@ -342,35 +349,16 @@ if ($opt_A) { open BRANCHES,">>", "$git_dir/svn2git"; -sub node_kind($$$) { - my ($branch, $path, $revision) = @_; +sub node_kind($$) { + my ($svnpath, $revision) = @_; my $pool=SVN::Pool->new; - my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool); + my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool); $pool->clear; return $kind; } -sub revert_split_path($$) { - my($branch,$path) = @_; - - my $svnpath; - $path = "" if $path eq "/"; # this should not happen, but ... - if($branch eq "/") { - $svnpath = "$trunk_name/$path"; - } elsif($branch =~ m#^/#) { - $svnpath = "$tag_name$branch/$path"; - } else { - $svnpath = "$branch_name/$branch/$path"; - } - - $svnpath =~ s#/+$##; - return $svnpath; -} - sub get_file($$$) { - my($rev,$branch,$path) = @_; - - my $svnpath = revert_split_path($branch,$path); + my($svnpath,$rev,$path) = @_; # now get it my ($name,$mode); @@ -413,10 +401,9 @@ sub get_file($$$) { } sub get_ignore($$$$$) { - my($new,$old,$rev,$branch,$path) = @_; + my($new,$old,$rev,$path,$svnpath) = @_; return unless $opt_I; - my $svnpath = revert_split_path($branch,$path); my $name = $svn->ignore("$svnpath",$rev); if ($path eq '/') { $path = $opt_I; @@ -435,7 +422,7 @@ sub get_ignore($$$$$) { close $F; unlink $name; push(@$new,['0644',$sha,$path]); - } else { + } elsif (defined $old) { push(@$old,$path); } } @@ -480,6 +467,27 @@ sub branch_rev($$) { return $therev; } +sub expand_svndir($$$); + +sub expand_svndir($$$) +{ + my ($svnpath, $rev, $path) = @_; + my @list; + get_ignore(\@list, undef, $rev, $path, $svnpath); + my $dirents = $svn->dir_list($svnpath, $rev); + foreach my $p(keys %$dirents) { + my $kind = node_kind($svnpath.'/'.$p, $rev); + if ($kind eq $SVN::Node::file) { + my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p); + push(@list, $f) if $f; + } elsif ($kind eq $SVN::Node::dir) { + push(@list, + expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p)); + } + } + return @list; +} + sub copy_path($$$$$$$$) { # Somebody copied a whole subdirectory. # We need to find the index entries from the old version which the @@ -488,8 +496,11 @@ sub copy_path($$$$$$$$) { my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_; my($srcbranch,$srcpath) = split_path($rev,$oldpath); - unless(defined $srcbranch) { - print "Path not found when copying from $oldpath @ $rev\n"; + unless(defined $srcbranch && defined $srcpath) { + print "Path not found when copying from $oldpath @ $rev.\n". + "Will try to copy from original SVN location...\n" + if $opt_v; + push (@$new, expand_svndir($oldpath, $rev, $path)); return; } my $therev = branch_rev($srcbranch, $rev); @@ -503,7 +514,7 @@ sub copy_path($$$$$$$$) { } print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v; if ($node_kind eq $SVN::Node::dir) { - $srcpath =~ s#/*$#/#; + $srcpath =~ s#/*$#/#; } my $pid = open my $f,'-|'; @@ -582,10 +593,12 @@ sub commit { if(defined $oldpath) { my $p; ($parent,$p) = split_path($revision,$oldpath); - if($parent eq "/") { - $parent = $opt_o; - } else { - $parent =~ s#^/##; # if it's a tag + if(defined $parent) { + if($parent eq "/") { + $parent = $opt_o; + } else { + $parent =~ s#^/##; # if it's a tag + } } } else { $parent = undef; @@ -651,9 +664,10 @@ sub commit { push(@old,$path); # remove any old stuff } if(($action->[0] eq "A") || ($action->[0] eq "R")) { - my $node_kind = node_kind($branch,$path,$revision); + my $node_kind = node_kind($action->[3], $revision); if ($node_kind eq $SVN::Node::file) { - my $f = get_file($revision,$branch,$path); + my $f = get_file($action->[3], + $revision, $path); if ($f) { push(@new,$f) if $f; } else { @@ -668,19 +682,20 @@ sub commit { \@new, \@parents); } else { get_ignore(\@new, \@old, $revision, - $branch, $path); + $path, $action->[3]); } } } elsif ($action->[0] eq "D") { push(@old,$path); } elsif ($action->[0] eq "M") { - my $node_kind = node_kind($branch,$path,$revision); + my $node_kind = node_kind($action->[3], $revision); if ($node_kind eq $SVN::Node::file) { - my $f = get_file($revision,$branch,$path); + my $f = get_file($action->[3], + $revision, $path); push(@new,$f) if $f; } elsif ($node_kind eq $SVN::Node::dir) { get_ignore(\@new, \@old, $revision, - $branch,$path); + $path, $action->[3]); } } else { die "$revision: unknown action '".$action->[0]."' for $path\n"; From 96779be48a8e5d3a50a07e0fcab942e7066235a9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Oct 2006 19:19:45 -0700 Subject: [PATCH 049/114] Fix git-revert Defaulting to $replay for the sake of fixing cherry-pick was not done conditionally, which broke git-revert. Noticed by Luben. Signed-off-by: Junio C Hamano --- git-revert.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-revert.sh b/git-revert.sh index 0784f74c18..4fd81b6ed6 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -7,9 +7,11 @@ case "$0" in *-revert* ) test -t 0 && edit=-e + replay= me=revert USAGE='[--edit | --no-edit] [-n] ' ;; *-cherry-pick* ) + replay=t edit= me=cherry-pick USAGE='[--edit] [-n] [-r] [-x] ' ;; @@ -18,7 +20,7 @@ case "$0" in esac . git-sh-setup -no_commit= replay=t +no_commit= while case "$#" in 0) break ;; esac do case "$1" in From 4e27fb06f0fe4206606172f94a18e08d7d87c070 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 6 Oct 2006 15:39:09 -0400 Subject: [PATCH 050/114] add commit count options to git-shortlog This patch does 3 things: 1) Output the number of commits along with the name for each author (nice to know for long lists spending more than a screen worth of commit lines). 2) Provide a switch (-n) to sort authors according to their number of commits instead of author alphabetic order. 3) Provide a switch (-s) to supress commit lines and only keep a summary of authors and the number of commits for each of them. And for good measure a short usage is displayed with -h. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Documentation/git-shortlog.txt | 17 +++++++++++-- git-shortlog.perl | 44 ++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 7486ebe785..1601d2267c 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -7,16 +7,29 @@ git-shortlog - Summarize 'git log' output SYNOPSIS -------- -git-log --pretty=short | 'git-shortlog' +git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] DESCRIPTION ----------- Summarizes 'git log' output in a format suitable for inclusion -in release announcements. Each commit will be grouped by author +in release announcements. Each commit will be grouped by author and the first line of the commit message will be shown. Additionally, "[PATCH]" will be stripped from the commit description. +OPTIONS +------- + +-h:: + Print a short usage message and exit. + +-n:: + Sort output according to the number of commits per author instead + of author alphabetic order. + +-s: + Supress commit description and Provide a commit count summary only. + FILES ----- '.mailmap':: diff --git a/git-shortlog.perl b/git-shortlog.perl index 0b14f833ee..334fec7477 100755 --- a/git-shortlog.perl +++ b/git-shortlog.perl @@ -1,6 +1,18 @@ #!/usr/bin/perl -w use strict; +use Getopt::Std; +use File::Basename qw(basename dirname); + +our ($opt_h, $opt_n, $opt_s); +getopts('hns'); + +$opt_h && usage(); + +sub usage { + print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < \n"; + exit(1); +} my (%mailmap); my (%email); @@ -38,16 +50,38 @@ sub by_name($$) { uc($a) cmp uc($b); } +sub by_nbentries($$) { + my ($a, $b) = @_; + my $a_entries = $map{$a}; + my $b_entries = $map{$b}; + + @$b_entries - @$a_entries || by_name $a, $b; +} + +my $sort_method = $opt_n ? \&by_nbentries : \&by_name; + +sub summary_output { + my ($obj, $num, $key); + + foreach $key (sort $sort_method keys %map) { + $obj = $map{$key}; + $num = @$obj; + printf "%s: %u\n", $key, $num; + $n_output += $num; + } +} sub shortlog_output { - my ($obj, $key, $desc); + my ($obj, $num, $key, $desc); + + foreach $key (sort $sort_method keys %map) { + $obj = $map{$key}; + $num = @$obj; - foreach $key (sort by_name keys %map) { # output author - printf "%s:\n", $key; + printf "%s (%u):\n", $key, $num; # output author's 1-line summaries - $obj = $map{$key}; foreach $desc (reverse @$obj) { print " $desc\n"; $n_output++; @@ -152,7 +186,7 @@ sub finalize { &setup_mailmap; &changelog_input; -&shortlog_output; +$opt_s ? &summary_output : &shortlog_output; &finalize; exit(0); From 63fba759bc1d9405f362e73918096815bf8e2a15 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Oct 2006 01:06:20 -0700 Subject: [PATCH 051/114] pack-objects: document --delta-base-offset option Signed-off-by: Junio C Hamano --- Documentation/git-pack-objects.txt | 13 ++++++++++++- builtin-pack-objects.c | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index d4661ddc2f..5788709710 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -9,7 +9,7 @@ git-pack-objects - Create a packed archive of objects SYNOPSIS -------- [verse] -'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty] +'git-pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list @@ -110,6 +110,17 @@ base-name:: This flag tells the command not to reuse existing deltas but compute them from scratch. +--delta-base-offset:: + A packed archive can express base object of a delta as + either 20-byte object name or as an offset in the + stream, but older version of git does not understand the + latter. By default, git-pack-objects only uses the + former format for better compatibility. This option + allows the command to use the latter format for + compactness. Depending on the average delta chain + length, this option typically shrinks the resulting + packfile by 3-5 per-cent. + Author ------ diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index ee5f031bc2..41e1e74533 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -15,7 +15,7 @@ #include #include -static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] Date: Tue, 10 Oct 2006 21:16:25 +0200 Subject: [PATCH 052/114] paginate git-diff by default --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index d7103a4086..e089b53571 100644 --- a/git.c +++ b/git.c @@ -226,7 +226,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "check-ref-format", cmd_check_ref_format }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "count-objects", cmd_count_objects, RUN_SETUP }, - { "diff", cmd_diff, RUN_SETUP }, + { "diff", cmd_diff, RUN_SETUP | USE_PAGER }, { "diff-files", cmd_diff_files, RUN_SETUP }, { "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-stages", cmd_diff_stages, RUN_SETUP }, From 9861718b305a672f3d17936ea6a6cbd9d9a70059 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Oct 2006 22:29:02 -0700 Subject: [PATCH 053/114] git-fetch --update-head-ok typofix Martin Waitz noticed that one of the case arms had an impossible choice. It turns out that what it was checking was redundant and the typo did not have any effect. Signed-off-by: Junio C Hamano --- git-fetch.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-fetch.sh b/git-fetch.sh index f1522bd49a..79222fbb1a 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -436,10 +436,10 @@ esac # If the original head was empty (i.e. no "master" yet), or # if we were told not to worry, we do not have to check. -case ",$update_head_ok,$orig_head," in -*,, | t,* ) +case "$orig_head" in +'') ;; -*) +?*) curr_head=$(git-rev-parse --verify HEAD 2>/dev/null) if test "$curr_head" != "$orig_head" then From a057f806674f691f1640d33df14de8de8a3d4c87 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Oct 2006 23:00:29 -0700 Subject: [PATCH 054/114] git-pull: we say commit X, not X commit. Signed-off-by: Junio C Hamano --- git-pull.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-pull.sh b/git-pull.sh index f380437997..ed04e7d8d8 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -58,7 +58,7 @@ then echo >&2 "Warning: fetch updated the current branch head." echo >&2 "Warning: fast forwarding your working tree from" - echo >&2 "Warning: $orig_head commit." + echo >&2 "Warning: commit $orig_head." git-update-index --refresh 2>/dev/null git-read-tree -u -m "$orig_head" "$curr_head" || die 'Cannot fast-forward your working tree. From 0503f9c178c36a19e1f8e8930b367db0f58ce5ca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 11 Oct 2006 07:57:17 +0000 Subject: [PATCH 055/114] git.spec.in: perl subpackage is installed in perl_vendorlib not vendorarch Signed-off-by: Junio C Hamano --- git.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.spec.in b/git.spec.in index 6d900342e3..9b1217ac3f 100644 --- a/git.spec.in +++ b/git.spec.in @@ -97,7 +97,7 @@ find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';' find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';' (find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@) > bin-man-doc-files -(find $RPM_BUILD_ROOT%{perl_vendorarch} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files +(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files %if %{!?_without_docs:1}0 (find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files %else From 6130259c30695d8a6ceda414a7d0d14183491e82 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 10 Oct 2006 08:58:23 -0600 Subject: [PATCH 056/114] Add --dry-run option to git-send-email Add a --dry-run option to git-send-email due to having made too many mistakes with it in the past week. I like having a safety catch on my machine gun. Signed-off-by: Matthew @ilcox Signed-off-by: Junio C Hamano --- git-send-email.perl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 3f50abaeb6..04c8942252 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -87,7 +87,8 @@ my (@to,@cc,@initial_cc,@bcclist, $initial_reply_to,$initial_subject,@files,$from,$compose,$time); # Behavior modification variables -my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0); +my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc, + $dry_run) = (1, 0, 0, 0, 0); my $smtp_server; # Example reply to: @@ -116,6 +117,7 @@ my $rc = GetOptions("from=s" => \$from, "quiet" => \$quiet, "suppress-from" => \$suppress_from, "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc, + "dry-run" => \$dry_run, ); # Verify the user input @@ -423,7 +425,9 @@ X-Mailer: git-send-email $gitversion $header .= "References: $references\n"; } - if ($smtp_server =~ m#^/#) { + if ($dry_run) { + # We don't want to send the email. + } elsif ($smtp_server =~ m#^/#) { my $pid = open my $sm, '|-'; defined $pid or die $!; if (!$pid) { From 9ac13ec941933c32849c2284b5d79ef608023a56 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 11 Oct 2006 11:49:15 -0400 Subject: [PATCH 057/114] atomic write for sideband remote messages It has been a few times that I ended up with such a confusing display: |remote: Generating pack... |remote: Done counting 17 objects. |remote: Result has 9 objects. |remote: Deltifying 9 objects. |remote: 100% (9/9) done |remote: Unpacking 9 objects |Total 9, written 9 (delta 8), reused 0 (delta 0) | 100% (9/9) done The confusion can be avoided in most cases by writing the remote message in one go to prevent interleacing with local messages. The buffer declaration has been moved inside recv_sideband() to avoid extra string copies. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-archive.c | 2 +- fetch-clone.c | 4 +--- sideband.c | 28 ++++++++++++++++------------ sideband.h | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 6dabdee201..9177379122 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -75,7 +75,7 @@ static int run_remote_archiver(const char *remote, int argc, die("git-archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ - rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf)); + rv = recv_sideband("archive", fd[0], 1, 2); close(fd[0]); rv |= finish_connect(pid); diff --git a/fetch-clone.c b/fetch-clone.c index b632ca0438..76b99afcdb 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -115,12 +115,10 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) die("%s: unable to fork off sideband demultiplexer", me); if (!side_pid) { /* subprocess */ - char buf[LARGE_PACKET_MAX]; - close(fd[0]); if (xd[0] != xd[1]) close(xd[1]); - if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf))) + if (recv_sideband(me, xd[0], fd[1], 2)) exit(1); exit(0); } diff --git a/sideband.c b/sideband.c index 1b14ff8892..277fa3c10d 100644 --- a/sideband.c +++ b/sideband.c @@ -11,10 +11,13 @@ * stream, aka "verbose"). A message over band #3 is a signal that * the remote died unexpectedly. A flush() concludes the stream. */ -int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz) +int recv_sideband(const char *me, int in_stream, int out, int err) { + char buf[7 + LARGE_PACKET_MAX + 1]; + strcpy(buf, "remote:"); while (1) { - int len = packet_read_line(in_stream, buf, bufsz); + int band, len; + len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { @@ -22,25 +25,26 @@ int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, in safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } + band = buf[7] & 0xff; len--; - switch (buf[0] & 0xFF) { + switch (band) { case 3: - safe_write(err, "remote: ", 8); - safe_write(err, buf+1, len); - safe_write(err, "\n", 1); + buf[7] = ' '; + buf[8+len] = '\n'; + safe_write(err, buf, 8+len+1); return SIDEBAND_REMOTE_ERROR; case 2: - safe_write(err, "remote: ", 8); - safe_write(err, buf+1, len); + buf[7] = ' '; + safe_write(err, buf, 8+len); continue; case 1: - safe_write(out, buf+1, len); + safe_write(out, buf+8, len); continue; default: - len = sprintf(buf + 1, + len = sprintf(buf, "%s: protocol error: bad band #%d\n", - me, buf[0] & 0xFF); - safe_write(err, buf+1, len); + me, band); + safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } } diff --git a/sideband.h b/sideband.h index 4872106fa0..a84b6917c7 100644 --- a/sideband.h +++ b/sideband.h @@ -7,7 +7,7 @@ #define DEFAULT_PACKET_MAX 1000 #define LARGE_PACKET_MAX 65520 -int recv_sideband(const char *me, int in_stream, int out, int err, char *, int); +int recv_sideband(const char *me, int in_stream, int out, int err); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif From c35b96e7851e417a24c1e255e353c67b31955466 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 11:53:21 -0700 Subject: [PATCH 058/114] git-svn: multi-init saves and reuses --tags and --branches arguments This should make it much easier to track newly added tags and branches. Re-running multi-init without command-line arguments should now detect new-tags and branches. --trunk shouldn't change often, but running multi-init on it is now idempotent. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index f5c7d46341..5a6c87e612 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -682,12 +682,17 @@ sub multi_init { } $_trunk = $url . $_trunk; } + my $ch_id; if ($GIT_SVN eq 'git-svn') { - print "GIT_SVN_ID set to 'trunk' for $_trunk\n"; + $ch_id = 1; $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; } init_vars(); - init($_trunk); + unless (-d $GIT_SVN_DIR) { + print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id; + init($_trunk); + sys('git-repo-config', 'svn.trunk', $_trunk); + } complete_url_ls_init($url, $_branches, '--branches/-b', ''); complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); } @@ -937,16 +942,21 @@ sub complete_url_ls_init { print STDERR "W: Unrecognized URL: $u\n"; die "This should never happen\n"; } + # don't try to init already existing refs my $id = $pfx.$1; - print "init $u => $id\n"; $GIT_SVN = $ENV{GIT_SVN_ID} = $id; init_vars(); - init($u); + unless (-d $GIT_SVN_DIR) { + print "init $u => $id\n"; + init($u); + } } exit 0; } waitpid $pid, 0; croak $? if $?; + my ($n) = ($switch =~ /^--(\w+)/); + sys('git-repo-config', "svn.$n", $var); } sub common_prefix { From 74a31a100ab96c37ebec5c04e1822bb2681bc345 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 11:53:22 -0700 Subject: [PATCH 059/114] git-svn: log command fixes Change the --verbose flag to more closely match svn. I was somehow under the impression that --summary included --raw diff output, but I was wrong. We now pass -r --raw --name-status as arguments if passed -v/--verbose. -r (recursive) is passed by default, since users usually want it, and accepting it causes difficulty with the -r option used by svn users. A --non-recursive switch has been added to disable this. Of course, --summary, --raw, -p and any other git-log options can still be passed directly (without --name-status). Also, several warnings about referencing undefined variables have been fixed. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 5a6c87e612..4f6e6a3bb8 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -52,7 +52,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m, - $_merge, $_strategy, $_dry_run, $_ignore_nodate); + $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive); my (@_branch_from, %tree_map, %users, %rusers, %equiv); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my @repo_path_split_cache; @@ -114,6 +114,7 @@ my %cmd = ( 'incremental' => \$_incremental, 'oneline' => \$_oneline, 'show-commit' => \$_show_commit, + 'non-recursive' => \$_non_recursive, 'authors-file|A=s' => \$_authors, } ], 'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees', @@ -752,13 +753,18 @@ sub show_log { # ignore } elsif (/^:\d{6} \d{6} $sha1_short/o) { push @{$c->{raw}}, $_; + } elsif (/^[ACRMDT]\t/) { + # we could add $SVN_PATH here, but that requires + # remote access at the moment (repo_path_split)... + s#^([ACRMDT])\t# $1 #; + push @{$c->{changed}}, $_; } elsif (/^diff /) { $d = 1; push @{$c->{diff}}, $_; } elsif ($d) { push @{$c->{diff}}, $_; } elsif (/^ (git-svn-id:.+)$/) { - (undef, $c->{r}, undef) = extract_metadata($1); + ($c->{url}, $c->{r}, undef) = extract_metadata($1); } elsif (s/^ //) { push @{$c->{l}}, $_; } @@ -850,7 +856,8 @@ sub git_svn_log_cmd { my ($r_min, $r_max) = @_; my @cmd = (qw/git-log --abbrev-commit --pretty=raw --default/, "refs/remotes/$GIT_SVN"); - push @cmd, '--summary' if $_verbose; + push @cmd, '-r' unless $_non_recursive; + push @cmd, qw/--raw --name-status/ if $_verbose; return @cmd unless defined $r_max; if ($r_max == $r_min) { push @cmd, '--max-count=1'; @@ -861,7 +868,7 @@ sub git_svn_log_cmd { my ($c_min, $c_max); $c_max = revdb_get($REVDB, $r_max); $c_min = revdb_get($REVDB, $r_min); - if ($c_min && $c_max) { + if (defined $c_min && defined $c_max) { if ($r_max > $r_max) { push @cmd, "$c_min..$c_max"; } else { @@ -2561,6 +2568,12 @@ sub show_commit { } } +sub show_commit_changed_paths { + my ($c) = @_; + return unless $c->{changed}; + print "Changed paths:\n", @{$c->{changed}}; +} + sub show_commit_normal { my ($c) = @_; print '-' x72, "\nr$c->{r} | "; @@ -2570,7 +2583,8 @@ sub show_commit_normal { my $nr_line = 0; if (my $l = $c->{l}) { - while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") { + while ($l->[$#$l] eq "\n" && $#$l > 0 + && $l->[($#$l - 1)] eq "\n") { pop @$l; } $nr_line = scalar @$l; @@ -2582,11 +2596,15 @@ sub show_commit_normal { } else { $nr_line .= ' lines'; } - print $nr_line, "\n\n"; + print $nr_line, "\n"; + show_commit_changed_paths($c); + print "\n"; print $_ foreach @$l; } } else { - print "1 line\n\n"; + print "1 line\n"; + show_commit_changed_paths($c); + print "\n"; } foreach my $x (qw/raw diff/) { From 34c06118ede69b37b76fcf71564f0f24234c5752 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Wed, 11 Oct 2006 22:31:15 +0200 Subject: [PATCH 060/114] gitweb: Fix search form when PATH_INFO is enabled Currently that was broken. Ideal fix would make the search form use PATH_INFO too, but it's just one insignificant place so it's no big deal if we don't for now... This at least makes it work. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 1d5217e334..0a7acfd493 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -408,6 +408,9 @@ sub href(%) { my %params = @_; my $href = $my_uri; + # XXX: Warning: If you touch this, check the search form for updating, + # too. + my @mapping = ( project => "p", action => "a", @@ -1442,6 +1445,7 @@ EOF } $cgi->param("a", "search"); $cgi->param("h", $search_hash); + $cgi->param("p", $project); print $cgi->startform(-method => "get", -action => $my_uri) . "
\n" . $cgi->hidden(-name => "p") . "\n" . From e8f5d9081cecff026913702dff8254d03e8f08b8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 14:53:35 -0700 Subject: [PATCH 061/114] Documentation/git-svn: document some of the newer features I've forgotten to document many of the features added along the way in the manpages. This fills in some holes in the documentation and adds updates some outdated information. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 139 +++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 16 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 1cfa3e342c..450ff1f85b 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -37,7 +37,9 @@ COMMANDS 'init':: Creates an empty git repository with additional metadata directories for git-svn. The Subversion URL must be specified - as a command-line argument. + as a command-line argument. Optionally, the target directory + to operate on can be specified as a second argument. Normally + this command initializes the current directory. 'fetch':: @@ -63,7 +65,30 @@ manually joining branches on commit. This is advantageous over 'commit' (below) because it produces cleaner, more linear history. +'log':: + This should make it easy to look up svn log messages when svn + users refer to -r/--revision numbers. + + The following features from `svn log' are supported: + + --revision=[:] - is supported, non-numeric args are not: + HEAD, NEXT, BASE, PREV, etc ... + -v/--verbose - it's not completely compatible with + the --verbose output in svn log, but + reasonably close. + --limit= - is NOT the same as --max-count, + doesn't count merged/excluded commits + --incremental - supported + + New features: + + --show-commit - shows the git commit sha1, as well + --oneline - our version of --pretty=oneline + + Any other arguments are passed directly to `git log' + 'commit':: + You should consider using 'dcommit' instead of this command. Commit specified commit or tree objects to SVN. This relies on your imported fetch data being up-to-date. This makes absolutely no attempts to do patching when committing to SVN, it @@ -86,12 +111,49 @@ manually joining branches on commit. directories. The output is suitable for appending to the $GIT_DIR/info/exclude file. +'commit-diff':: + Commits the diff of two tree-ish arguments from the + command-line. This command is intended for interopability with + git-svnimport and does not rely on being inside an git-svn + init-ed repository. This command takes three arguments, (a) the + original tree to diff against, (b) the new tree result, (c) the + URL of the target Subversion repository. The final argument + (URL) may be omitted if you are working from a git-svn-aware + repository (that has been init-ed with git-svn). + +'graft-branches':: + This command attempts to detect merges/branches from already + imported history. Techniques used currently include regexes, + file copies, and tree-matches). This command generates (or + modifies) the $GIT_DIR/info/grafts file. This command is + considered experimental, and inherently flawed because + merge-tracking in SVN is inherently flawed and inconsistent + across different repositories. + +'multi-init':: + This command supports git-svnimport-like command-line syntax for + importing repositories that are layed out as recommended by the + SVN folks. This is a bit more tolerant than the git-svnimport + command-line syntax and doesn't require the user to figure out + where the repository URL ends and where the repository path + begins. + +'multi-fetch':: + This runs fetch on all known SVN branches we're tracking. This + will NOT discover new branches (unlike git-svnimport), so + multi-init will need to be re-run (it's idempotent). + -- OPTIONS ------- -- +--shared:: +--template=:: + Only used with the 'init' command. + These are passed directly to gitlink:git-init-db[1]. + -r :: --revision :: @@ -115,7 +177,7 @@ git-rev-list --pretty=oneline output can be used. --rmdir:: -Only used with the 'commit' command. +Only used with the 'dcommit', 'commit' and 'commit-diff' commands. Remove directories from the SVN tree if there are no files left behind. SVN can version empty directories, and they are not @@ -128,7 +190,7 @@ repo-config key: svn.rmdir -e:: --edit:: -Only used with the 'commit' command. +Only used with the 'dcommit', 'commit' and 'commit-diff' commands. Edit the commit message before committing to SVN. This is off by default for objects that are commits, and forced on when committing @@ -139,7 +201,7 @@ repo-config key: svn.edit -l:: --find-copies-harder:: -Both of these are only used with the 'commit' command. +Only used with the 'dcommit', 'commit' and 'commit-diff' commands. They are both passed directly to git-diff-tree see gitlink:git-diff-tree[1] for more information. @@ -164,7 +226,26 @@ will abort operation. The user will then have to add the appropriate entry. Re-running the previous git-svn command after the authors-file is modified should continue operation. -repo-config key: svn.authors-file +repo-config key: svn.authorsfile + +-q:: +--quiet:: + Make git-svn less verbose. This only affects git-svn if you + have the SVN::* libraries installed and are using them. + +--repack[=]:: +--repack-flags= + These should help keep disk usage sane for large fetches + with many revisions. + + --repack takes an optional argument for the number of revisions + to fetch before repacking. This defaults to repacking every + 1000 commits fetched if no argument is specified. + + --repack-flags are passed directly to gitlink:git-repack[1]. + +repo-config key: svn.repack +repo-config key: svn.repackflags -m:: --merge:: @@ -215,6 +296,28 @@ section on '<>' for more information on using GIT_SVN_ID. +--follow-parent:: + This is especially helpful when we're tracking a directory + that has been moved around within the repository, or if we + started tracking a branch and never tracked the trunk it was + descended from. + + This relies on the SVN::* libraries to work. + +repo-config key: svn.followparent + +--no-metadata:: + This gets rid of the git-svn-id: lines at the end of every commit. + + With this, you lose the ability to use the rebuild command. If + you ever lose your .git/svn/git-svn/.rev_db file, you won't be + able to fetch again, either. This is fine for one-shot imports. + + The 'git-svn log' command will not work on repositories using this, + either. + +repo-config key: svn.nometadata + -- COMPATIBILITY OPTIONS @@ -231,6 +334,9 @@ for tracking the remote. --no-ignore-externals:: Only used with the 'fetch' and 'rebuild' command. +This command has no effect when you are using the SVN::* +libraries with git, svn:externals are always avoided. + By default, git-svn passes --ignore-externals to svn to avoid fetching svn:external trees into git. Pass this flag to enable externals tracking directly via git. @@ -264,7 +370,7 @@ Basic Examples Tracking and contributing to an Subversion managed-project: ------------------------------------------------------------------------ -# Initialize a tree (like git init-db): +# Initialize a repo (like git init-db): git-svn init http://svn.foo.org/project/trunk # Fetch remote revisions: git-svn fetch @@ -312,8 +418,8 @@ branches or directories in a Subversion repository, git-svn has a simple hack to allow it to track an arbitrary number of related _or_ unrelated SVN repositories via one git repository. Simply set the GIT_SVN_ID environment variable to a name other other than "git-svn" (the default) -and git-svn will ignore the contents of the $GIT_DIR/git-svn directory -and instead do all of its work in $GIT_DIR/$GIT_SVN_ID for that +and git-svn will ignore the contents of the $GIT_DIR/svn/git-svn directory +and instead do all of its work in $GIT_DIR/svn/$GIT_SVN_ID for that invocation. The interface branch will be remotes/$GIT_SVN_ID, instead of remotes/git-svn. Any remotes/$GIT_SVN_ID branch should never be modified by the user outside of git-svn commands. @@ -341,6 +447,9 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD: Advanced Example: Tracking a Reorganized Repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note: this example is now obsolete if you have SVN::* libraries +installed. Simply use --follow-parent when fetching. + If you're tracking a directory that has moved, or otherwise been branched or tagged off of another directory in the repository and you care about the full history of the project, then you can read this @@ -371,20 +480,18 @@ he needed to continue tracking /ufoai/trunk where /trunk left off. BUGS ---- -If somebody commits a conflicting changeset to SVN at a bad moment -(right before you commit) causing a conflict and your commit to fail, -your svn working tree ($GIT_DIR/git-svn/tree) may be dirtied. The -easiest thing to do is probably just to rm -rf $GIT_DIR/git-svn/tree and -run 'rebuild'. + +If you are not using the SVN::* Perl libraries and somebody commits a +conflicting changeset to SVN at a bad moment (right before you commit) +causing a conflict and your commit to fail, your svn working tree +($GIT_DIR/git-svn/tree) may be dirtied. The easiest thing to do is +probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'. We ignore all SVN properties except svn:executable. Too difficult to map them since we rely heavily on git write-tree being _exactly_ the same on both the SVN and git working trees and I prefer not to clutter working trees with metadata files. -svn:keywords can't be ignored in Subversion (at least I don't know of -a way to ignore them). - Renamed and copied directories are not detected by git and hence not tracked when committing to SVN. I do not plan on adding support for this as it's quite difficult and time-consuming to get working for all From b203b769f2f382307f21b4a021ce08f9a4cfa94c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 14:53:36 -0700 Subject: [PATCH 062/114] git-svn: -h(elp) message formatting fixes 'graft-branches' is slightly longer than the rest of the commands, so the text was squished together in the formatted output. This patch just adds some more whitespace to make the text look more pleasant. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 4f6e6a3bb8..84d2c58e9a 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -169,11 +169,11 @@ Usage: $0 [options] [arguments]\n foreach (sort keys %cmd) { next if $cmd && $cmd ne $_; - print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n"; + print $fd ' ',pack('A17',$_),$cmd{$_}->[1],"\n"; foreach (keys %{$cmd{$_}->[2]}) { # prints out arguments as they should be passed: my $x = s#[:=]s$## ? '' : s#[:=]i$## ? '' : ''; - print $fd ' ' x 17, join(', ', map { length $_ > 1 ? + print $fd ' ' x 21, join(', ', map { length $_ > 1 ? "--$_" : "-$_" } split /\|/,$_)," $x\n"; } From 14763e7bda06a502455ef4420067205797b9a907 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 16:16:02 -0700 Subject: [PATCH 063/114] commit: fix a segfault when displaying a commit with unreachable parents I was running git show on various commits found by fsck-objects when I found this bug. Since find_unique_abbrev() cannot find an abbreviation for an object not in the database, it will return NULL, which is bad to run strlen() on. So instead, we'll just display the unabbreviated sha1 that we referenced in the commit. I'm not sure that this is the best 'fix' for it because the commit I was trying to show was broken, but I don't think a program should segfault even if the user tries to do something stupid. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- commit.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/commit.c b/commit.c index 5b6e082c85..a6d543eee7 100644 --- a/commit.c +++ b/commit.c @@ -548,10 +548,13 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com while (parent) { struct commit *p = parent->item; - const char *hex = abbrev - ? find_unique_abbrev(p->object.sha1, abbrev) - : sha1_to_hex(p->object.sha1); - const char *dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + const char *hex = NULL; + const char *dots; + if (abbrev) + hex = find_unique_abbrev(p->object.sha1, abbrev); + if (!hex) + hex = sha1_to_hex(p->object.sha1); + dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; offset += sprintf(buf + offset, " %s%s", hex, dots); From 83e9940a5ef79cedc36a24a4a89d097abf1b28c7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 18:19:55 -0700 Subject: [PATCH 064/114] git-svn: add a message encouraging use of SVN::* libraries I'm using svn 1.4.0-4 in Debian unstable and apparently there's a regression on the SVN side that prevents a symlink from becoming a regular file (which git supports, of course). It's not a noticeable regression for most people, but this broke the full-svn-tests target in t/Makefile for me. The SVN::* Perl libraries seem to have matured and improved over the past year, and git-svn has supported them for several months now, so with that I encourage all users to start using the SVN::* Perl libraries with git-svn. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/git-svn.perl b/git-svn.perl index 84d2c58e9a..a128d90fc0 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -40,8 +40,22 @@ memoize('cmt_metadata'); memoize('get_commit_time'); my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); + +sub nag_lib { + print STDERR < Date: Thu, 12 Oct 2006 02:57:39 -0700 Subject: [PATCH 065/114] apply --numstat -z: line termination fix. Signed-off-by: Junio C Hamano --- builtin-apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-apply.c b/builtin-apply.c index de5f855266..e3ef04477c 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2112,7 +2112,7 @@ static void numstat_patch_list(struct patch *patch) quote_c_style(name, NULL, stdout, 0); else fputs(name, stdout); - putchar('\n'); + putchar(line_termination); } } From 2344d47fba45bdddb0801ebf0789935a96e44352 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 12 Oct 2006 14:22:14 +0200 Subject: [PATCH 066/114] diff: fix 2 whitespace issues When whitespace or whitespace change was ignored, the function xdl_recmatch() returned memcmp() style differences, which is wrong, since it should return 0 on non-match. Also, there were three horrible off-by-one bugs, even leading to wrong hashes in the whitespace special handling. The issue was noticed by Ray Lehtiniemi. For good measure, this commit adds a test. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- t/t4015-diff-whitespace.sh | 122 +++++++++++++++++++++++++++++++++++++ xdiff/xutils.c | 29 ++++----- 2 files changed, 134 insertions(+), 17 deletions(-) create mode 100755 t/t4015-diff-whitespace.sh diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh new file mode 100755 index 0000000000..c945085245 --- /dev/null +++ b/t/t4015-diff-whitespace.sh @@ -0,0 +1,122 @@ +#!/bin/sh +# +# Copyright (c) 2006 Johannes E. Schindelin +# + +test_description='Test special whitespace in diff engine. + +' +. ./test-lib.sh +. ../diff-lib.sh + +# Ray Lehtiniemi's example + +cat << EOF > x +do { + nothing; +} while (0); +EOF + +git-update-index --add x + +cat << EOF > x +do +{ + nothing; +} +while (0); +EOF + +cat << EOF > expect +diff --git a/x b/x +index adf3937..6edc172 100644 +--- a/x ++++ b/x +@@ -1,3 +1,5 @@ +-do { ++do ++{ + nothing; +-} while (0); ++} ++while (0); +EOF + +git-diff > out +test_expect_success "Ray's example without options" 'diff -u expect out' + +git-diff -w > out +test_expect_success "Ray's example with -w" 'diff -u expect out' + +git-diff -b > out +test_expect_success "Ray's example with -b" 'diff -u expect out' + +cat << EOF > x +whitespace at beginning +whitespace change +whitespace in the middle +whitespace at end +unchanged line +CR at end +EOF + +git-update-index x + +cat << EOF > x + whitespace at beginning +whitespace change +white space in the middle +whitespace at end +unchanged line +CR at end +EOF + +cat << EOF > expect +diff --git a/x b/x +index d99af23..8b32fb5 100644 +--- a/x ++++ b/x +@@ -1,6 +1,6 @@ +-whitespace at beginning +-whitespace change +-whitespace in the middle +-whitespace at end ++ whitespace at beginning ++whitespace change ++white space in the middle ++whitespace at end + unchanged line +-CR at end ++CR at end +EOF +git-diff > out +test_expect_success 'another test, without options' 'diff -u expect out' + +cat << EOF > expect +diff --git a/x b/x +index d99af23..8b32fb5 100644 +EOF +git-diff -w > out +test_expect_success 'another test, with -w' 'diff -u expect out' + +cat << EOF > expect +diff --git a/x b/x +index d99af23..8b32fb5 100644 +--- a/x ++++ b/x +@@ -1,6 +1,6 @@ +-whitespace at beginning ++ whitespace at beginning + whitespace change +-whitespace in the middle +-whitespace at end ++white space in the middle ++whitespace at end + unchanged line +-CR at end ++CR at end +EOF +git-diff -b > out +test_expect_success 'another test, with -b' 'diff -u expect out' + +test_done diff --git a/xdiff/xutils.c b/xdiff/xutils.c index f7bdd395ad..9e4bb47ee9 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -191,36 +191,30 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) int i1, i2; if (flags & XDF_IGNORE_WHITESPACE) { - for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) { + for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) { if (isspace(l1[i1])) while (isspace(l1[i1]) && i1 < s1) i1++; - else if (isspace(l2[i2])) + if (isspace(l2[i2])) while (isspace(l2[i2]) && i2 < s2) i2++; - else if (l1[i1] != l2[i2]) - return l2[i2] - l1[i1]; + if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++]) + return 0; } - if (i1 >= s1) - return 1; - else if (i2 >= s2) - return -1; + return (i1 >= s1 && i2 >= s2); } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { - for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) { + for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) { if (isspace(l1[i1])) { if (!isspace(l2[i2])) - return -1; + return 0; while (isspace(l1[i1]) && i1 < s1) i1++; while (isspace(l2[i2]) && i2 < s2) i2++; - } else if (l1[i1] != l2[i2]) - return l2[i2] - l1[i1]; + } else if (l1[i1++] != l2[i2++]) + return 0; } - if (i1 >= s1) - return 1; - else if (i2 >= s2) - return -1; + return (i1 >= s1 && i2 >= s2); } else return s1 == s2 && !memcmp(l1, l2, s1); @@ -233,7 +227,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) { for (; ptr < top && *ptr != '\n'; ptr++) { if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) { - while (ptr < top && isspace(*ptr) && ptr[1] != '\n') + while (ptr + 1 < top && isspace(ptr[1]) + && ptr[1] != '\n') ptr++; if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { ha += (ha << 5); From 23bed43d0c5190b6da89f3f34d8badcc1b3c304b Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Thu, 12 Oct 2006 18:26:34 +0200 Subject: [PATCH 067/114] Documentation: add missing second colons and remove a typo It takes two colons to mark text as item label. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/git-http-push.txt | 2 +- Documentation/git-send-pack.txt | 2 +- Documentation/git-shortlog.txt | 4 ++-- Documentation/glossary.txt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt index 7e1f894a92..c2485c6e9c 100644 --- a/Documentation/git-http-push.txt +++ b/Documentation/git-http-push.txt @@ -34,7 +34,7 @@ OPTIONS Report the list of objects being walked locally and the list of objects successfully sent to the remote repository. -...: +...:: The remote refs to update. diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index 9e67f17302..5376f68548 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -43,7 +43,7 @@ OPTIONS :: The repository to update. -...: +...:: The remote refs to update. diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 1601d2267c..d54fc3e5c6 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -27,8 +27,8 @@ OPTIONS Sort output according to the number of commits per author instead of author alphabetic order. --s: - Supress commit description and Provide a commit count summary only. +-s:: + Supress commit description and provide a commit count summary only. FILES ----- diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 14449ca8ba..7e560b0eea 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -179,7 +179,7 @@ object name:: character hexadecimal encoding of the hash of the object (possibly followed by a white space). -object type: +object type:: One of the identifiers "commit","tree","tag" and "blob" describing the type of an object. @@ -324,7 +324,7 @@ tag:: A tag is most typically used to mark a particular point in the commit ancestry chain. -unmerged index: +unmerged index:: An index which contains unmerged index entries. working tree:: From 4035b46e12ee6e5c9339e263cd16c2e2422b8928 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Oct 2006 14:20:27 -0700 Subject: [PATCH 068/114] t4015: work-around here document problem on Cygwin. Signed-off-by: Junio C Hamano --- t/t4015-diff-whitespace.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index c945085245..1bc5b7a412 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -51,13 +51,13 @@ test_expect_success "Ray's example with -w" 'diff -u expect out' git-diff -b > out test_expect_success "Ray's example with -b" 'diff -u expect out' -cat << EOF > x +tr 'Q' '\015' << EOF > x whitespace at beginning whitespace change whitespace in the middle whitespace at end unchanged line -CR at end +CR at endQ EOF git-update-index x @@ -71,7 +71,7 @@ unchanged line CR at end EOF -cat << EOF > expect +tr 'Q' '\015' << EOF > expect diff --git a/x b/x index d99af23..8b32fb5 100644 --- a/x @@ -86,7 +86,7 @@ index d99af23..8b32fb5 100644 +white space in the middle +whitespace at end unchanged line --CR at end +-CR at endQ +CR at end EOF git-diff > out @@ -99,7 +99,7 @@ EOF git-diff -w > out test_expect_success 'another test, with -w' 'diff -u expect out' -cat << EOF > expect +tr 'Q' '\015' << EOF > expect diff --git a/x b/x index d99af23..8b32fb5 100644 --- a/x @@ -113,7 +113,7 @@ index d99af23..8b32fb5 100644 +white space in the middle +whitespace at end unchanged line --CR at end +-CR at endQ +CR at end EOF git-diff -b > out From b6945f570acef7603c58d2aec4a84dce006fbd65 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Oct 2006 21:28:58 -0700 Subject: [PATCH 069/114] git-repack: repo.usedeltabaseoffset When configuration variable `repack.UseDeltaBaseOffset` is set for the repository, the command passes `--delta-base-offset` option to `git-pack-objects`; this typically results in slightly smaller packs, but the generated packs are incompatible with versions of git older than (and including) v1.4.3. We will make it default to true sometime in the future, but not for a while. Signed-off-by: Junio C Hamano --- Documentation/config.txt | 4 ++++ Documentation/git-repack.txt | 14 ++++++++++++++ git-repack.sh | 11 ++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 98c1f3e2e3..88e0bf00c2 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -224,6 +224,10 @@ pull.octopus:: pull.twohead:: The default merge strategy to use when pulling a single branch. +repack.usedeltabaseoffset:: + Allow gitlink:git-repack[1] to create packs that uses + delta-base offset. Defaults to false. + show.difftree:: The default gitlink:git-diff-tree[1] arguments to be used for gitlink:git-show[1]. diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 49f7e0a4a4..4e6631a27f 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -66,6 +66,20 @@ OPTIONS to be applied that many times to get to the necessary object. +Configuration +------------- + +When configuration variable `repack.UseDeltaBaseOffset` is set +for the repository, the command passes `--delta-base-offset` +option to `git-pack-objects`; this typically results in slightly +smaller packs, but the generated packs are incompatible with +versions of git older than (and including) v1.4.3; do not set +the variable in a repository that older version of git needs to +be able to read (this includes repositories from which packs can +be copied out over http or rsync, and people who obtained packs +that way can try to use older git with it). + + Author ------ Written by Linus Torvalds diff --git a/git-repack.sh b/git-repack.sh index b525fc5dfd..2a214891a8 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Linus Torvalds # -USAGE='[-a] [-d] [-f] [-l] [-n] [-q]' +USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]' . git-sh-setup no_update_info= all_into_one= remove_redundant= @@ -24,6 +24,15 @@ do shift done +# Later we will default repack.UseDeltaBaseOffset to true +default_dbo=false + +case "`git repo-config --bool repack.usedeltabaseoffset || + echo $default_dbo`" in +true) + extra="$extra --delta-base-offset" ;; +esac + PACKDIR="$GIT_OBJECT_DIRECTORY/pack" PACKTMP="$GIT_DIR/.tmp-$$-pack" rm -f "$PACKTMP"-* From 74e2abe5b70aadf06984472ba9aa9a29900040e6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 12 Oct 2006 03:01:00 -0700 Subject: [PATCH 070/114] diff --numstat [jc: with documentation from Jakub] Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 5 +++++ combine-diff.c | 9 ++++++--- diff.c | 29 +++++++++++++++++++++++++++-- diff.h | 16 +++++++++------- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 7b7b9e8ce9..e112172ca5 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -16,6 +16,11 @@ The width of the filename part can be controlled by giving another width to it separated by a comma. +--numstat:: + Similar to \--stat, but shows number of added and + deleted lines in decimal notation and pathname without + abbreviation, to make it more machine friendly. + --summary:: Output a condensed summary of extended header information such as creations, renames and mode changes. diff --git a/combine-diff.c b/combine-diff.c index 46d9121baf..65c786807b 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -856,8 +856,10 @@ void diff_tree_combined(const unsigned char *sha1, /* show stat against the first parent even * when doing combined diff. */ - if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT) - diffopts.output_format = DIFF_FORMAT_DIFFSTAT; + int stat_opt = (opt->output_format & + (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT)); + if (i == 0 && stat_opt) + diffopts.output_format = stat_opt; else diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_tree_sha1(parent[i], sha1, "", &diffopts); @@ -887,7 +889,8 @@ void diff_tree_combined(const unsigned char *sha1, } needsep = 1; } - else if (opt->output_format & DIFF_FORMAT_DIFFSTAT) + else if (opt->output_format & + (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT)) needsep = 1; if (opt->output_format & DIFF_FORMAT_PATCH) { if (needsep) diff --git a/diff.c b/diff.c index fb8243261c..2dcad1942c 100644 --- a/diff.c +++ b/diff.c @@ -795,6 +795,23 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) set, total_files, adds, dels, reset); } +static void show_numstat(struct diffstat_t* data, struct diff_options *options) +{ + int i; + + for (i = 0; i < data->nr; i++) { + struct diffstat_file *file = data->files[i]; + + printf("%d\t%d\t", file->added, file->deleted); + if (options->line_termination && + quote_c_style(file->name, NULL, NULL, 0)) + quote_c_style(file->name, NULL, stdout, 0); + else + fputs(file->name, stdout); + putchar(options->line_termination); + } +} + struct checkdiff_t { struct xdiff_emit_state xm; const char *filename; @@ -1731,6 +1748,7 @@ int diff_setup_done(struct diff_options *options) DIFF_FORMAT_CHECKDIFF | DIFF_FORMAT_NO_OUTPUT)) options->output_format &= ~(DIFF_FORMAT_RAW | + DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH); @@ -1740,6 +1758,7 @@ int diff_setup_done(struct diff_options *options) * recursive bits for other formats here. */ if (options->output_format & (DIFF_FORMAT_PATCH | + DIFF_FORMAT_NUMSTAT | DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_CHECKDIFF)) options->recursive = 1; @@ -1828,6 +1847,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--patch-with-raw")) { options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; } + else if (!strcmp(arg, "--numstat")) { + options->output_format |= DIFF_FORMAT_NUMSTAT; + } else if (!strncmp(arg, "--stat", 6)) { char *end; int width = options->stat_width; @@ -2602,7 +2624,7 @@ void diff_flush(struct diff_options *options) separator++; } - if (output_format & DIFF_FORMAT_DIFFSTAT) { + if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) { struct diffstat_t diffstat; memset(&diffstat, 0, sizeof(struct diffstat_t)); @@ -2612,7 +2634,10 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } - show_stats(&diffstat, options); + if (output_format & DIFF_FORMAT_NUMSTAT) + show_numstat(&diffstat, options); + if (output_format & DIFF_FORMAT_DIFFSTAT) + show_stats(&diffstat, options); separator++; } diff --git a/diff.h b/diff.h index b48c9914e7..ce3058e437 100644 --- a/diff.h +++ b/diff.h @@ -26,20 +26,21 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_FORMAT_RAW 0x0001 #define DIFF_FORMAT_DIFFSTAT 0x0002 -#define DIFF_FORMAT_SUMMARY 0x0004 -#define DIFF_FORMAT_PATCH 0x0008 +#define DIFF_FORMAT_NUMSTAT 0x0004 +#define DIFF_FORMAT_SUMMARY 0x0008 +#define DIFF_FORMAT_PATCH 0x0010 /* These override all above */ -#define DIFF_FORMAT_NAME 0x0010 -#define DIFF_FORMAT_NAME_STATUS 0x0020 -#define DIFF_FORMAT_CHECKDIFF 0x0040 +#define DIFF_FORMAT_NAME 0x0100 +#define DIFF_FORMAT_NAME_STATUS 0x0200 +#define DIFF_FORMAT_CHECKDIFF 0x0400 /* Same as output_format = 0 but we know that -s flag was given * and we should not give default value to output_format. */ -#define DIFF_FORMAT_NO_OUTPUT 0x0080 +#define DIFF_FORMAT_NO_OUTPUT 0x0800 -#define DIFF_FORMAT_CALLBACK 0x0100 +#define DIFF_FORMAT_CALLBACK 0x1000 struct diff_options { const char *filter; @@ -170,6 +171,7 @@ extern void diffcore_std_no_resolve(struct diff_options *); " --patch-with-raw\n" \ " output both a patch and the diff-raw format.\n" \ " --stat show diffstat instead of patch.\n" \ +" --numstat show numeric diffstat instead of patch.\n" \ " --patch-with-stat\n" \ " output a patch and prepend its diffstat.\n" \ " --name-only show only names of changed files.\n" \ From ce91fc6eb9c4aefc73f845fc7104f332b2b308d2 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 14 Oct 2006 02:02:37 -0700 Subject: [PATCH 071/114] git-svn: fix commits over svn+ssh:// Once a get_commit_editor has been called from an SVN session, RA layer operations are not allowed (well, unless you're using file:// or http(s)://). So we'll pass an alternate SVN::Ra object to our editor object for running 'check-path'. This should fix commits over svnserve (svn:// without ssh, too). Closes Debian bug #392702, thanks to Pierre Habouzit for reporting the bug. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index a128d90fc0..0f968c8cea 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -536,7 +536,7 @@ sub commit_lib { $SVN = libsvn_connect($repo); my $ed = SVN::Git::Editor->new( { r => $r_last, - ra => $SVN, + ra => $SVN_LOG, c => $c, svn_path => $SVN_PATH }, @@ -832,7 +832,7 @@ sub commit_diff { $SVN ||= libsvn_connect($repo); my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum, - ra => $SVN, c => $tb, + ra => $SVN_LOG, c => $tb, svn_path => $SVN_PATH }, $SVN->get_commit_editor($_message, From abdc3fc84216a9334f40f3b7ce13e20a825697d0 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 14 Oct 2006 12:45:36 +0200 Subject: [PATCH 072/114] Add hash_sha1_file() Most callers of write_sha1_file_prepare() are only interested in the resulting hash but don't care about the returned file name or the header. This patch adds a simple wrapper named hash_sha1_file() which does just that, and converts potential callers. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- builtin-apply.c | 8 ++------ cache-tree.c | 8 ++------ cache.h | 1 + merge-recursive.c | 5 +---- sha1_file.c | 34 ++++++++++++++++------------------ 5 files changed, 22 insertions(+), 34 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index e3ef04477c..cbe597771b 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1783,8 +1783,6 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) { const char *name = patch->old_name ? patch->old_name : patch->new_name; unsigned char sha1[20]; - unsigned char hdr[50]; - int hdrlen; /* For safety, we require patch index line to contain * full 40-byte textual SHA1 for old and new, at least for now. @@ -1800,8 +1798,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) /* See if the old one matches what the patch * applies to. */ - write_sha1_file_prepare(desc->buffer, desc->size, - blob_type, sha1, hdr, &hdrlen); + hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix)) return error("the patch applies to '%s' (%s), " "which does not match the " @@ -1846,8 +1843,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch) name); /* verify that the result matches */ - write_sha1_file_prepare(desc->buffer, desc->size, blob_type, - sha1, hdr, &hdrlen); + hash_sha1_file(desc->buffer, desc->size, blob_type, sha1); if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1)); } diff --git a/cache-tree.c b/cache-tree.c index 323c68a670..d388848dd2 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -344,12 +344,8 @@ static int update_one(struct cache_tree *it, #endif } - if (dryrun) { - unsigned char hdr[200]; - int hdrlen; - write_sha1_file_prepare(buffer, offset, tree_type, it->sha1, - hdr, &hdrlen); - } + if (dryrun) + hash_sha1_file(buffer, offset, tree_type, it->sha1); else write_sha1_file(buffer, offset, tree_type, it->sha1); free(buffer); diff --git a/cache.h b/cache.h index 97debd03c5..aa3a562759 100644 --- a/cache.h +++ b/cache.h @@ -245,6 +245,7 @@ char *enter_repo(char *path, int strict); extern int sha1_object_info(const unsigned char *, char *, unsigned long *); extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size); extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size); +extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1); extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1); extern char *write_sha1_file_prepare(void *buf, unsigned long len, diff --git a/merge-recursive.c b/merge-recursive.c index 611cd95cf5..2ba43ae84b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1235,13 +1235,10 @@ int merge(struct commit *h1, if (merged_common_ancestors == NULL) { /* if there is no common ancestor, make an empty tree */ struct tree *tree = xcalloc(1, sizeof(struct tree)); - unsigned char hdr[40]; - int hdrlen; tree->object.parsed = 1; tree->object.type = OBJ_TREE; - write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1, - hdr, &hdrlen); + hash_sha1_file(NULL, 0, tree_type, tree->object.sha1); merged_common_ancestors = make_virtual_commit(tree, "ancestor"); } diff --git a/sha1_file.c b/sha1_file.c index 27b1ebb720..6c64ec41c3 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1501,6 +1501,15 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon stream->avail_out -= hdr; } +int hash_sha1_file(void *buf, unsigned long len, const char *type, + unsigned char *sha1) +{ + unsigned char hdr[50]; + int hdrlen; + write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); + return 0; +} + int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; @@ -1784,8 +1793,6 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) unsigned long size = 4096; char *buf = xmalloc(size); int ret; - unsigned char hdr[50]; - int hdrlen; if (read_pipe(fd, &buf, &size)) { free(buf); @@ -1796,10 +1803,8 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) type = blob_type; if (write_object) ret = write_sha1_file(buf, size, type, sha1); - else { - write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen); - ret = 0; - } + else + ret = hash_sha1_file(buf, size, type, sha1); free(buf); return ret; } @@ -1809,8 +1814,6 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con unsigned long size = st->st_size; void *buf; int ret; - unsigned char hdr[50]; - int hdrlen; buf = ""; if (size) @@ -1823,10 +1826,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con type = blob_type; if (write_object) ret = write_sha1_file(buf, size, type, sha1); - else { - write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen); - ret = 0; - } + else + ret = hash_sha1_file(buf, size, type, sha1); if (size) munmap(buf, size); return ret; @@ -1855,12 +1856,9 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write return error("readlink(\"%s\"): %s", path, errstr); } - if (!write_object) { - unsigned char hdr[50]; - int hdrlen; - write_sha1_file_prepare(target, st->st_size, blob_type, - sha1, hdr, &hdrlen); - } else if (write_sha1_file(target, st->st_size, blob_type, sha1)) + if (!write_object) + hash_sha1_file(target, st->st_size, blob_type, sha1); + else if (write_sha1_file(target, st->st_size, blob_type, sha1)) return error("%s: failed to insert into database", path); free(target); From 8f9777801d09b195e1820061914ec27fcd5975c8 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 14 Oct 2006 12:45:45 +0200 Subject: [PATCH 073/114] Make write_sha1_file_prepare() static There are no callers of write_sha1_file_prepare() left outside of sha1_file.c, so make it static. Signed-off-by: Junio C Hamano --- cache.h | 6 ------ sha1_file.c | 9 +++------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/cache.h b/cache.h index aa3a562759..c35470107d 100644 --- a/cache.h +++ b/cache.h @@ -247,12 +247,6 @@ extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, uns extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size); extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1); extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1); -extern char *write_sha1_file_prepare(void *buf, - unsigned long len, - const char *type, - unsigned char *sha1, - unsigned char *hdr, - int *hdrlen); extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type); diff --git a/sha1_file.c b/sha1_file.c index 6c64ec41c3..d111be74a3 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1347,12 +1347,9 @@ void *read_object_with_reference(const unsigned char *sha1, } } -char *write_sha1_file_prepare(void *buf, - unsigned long len, - const char *type, - unsigned char *sha1, - unsigned char *hdr, - int *hdrlen) +static char *write_sha1_file_prepare(void *buf, unsigned long len, + const char *type, unsigned char *sha1, + unsigned char *hdr, int *hdrlen) { SHA_CTX c; From 6844fc806ace7d7c31ad788a8886cfd0498ceec5 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 14 Oct 2006 16:05:25 +0200 Subject: [PATCH 074/114] Fix tracing when GIT_TRACE is set to an empty string. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trace.c b/trace.c index f9efc918b8..495e5ed92a 100644 --- a/trace.c +++ b/trace.c @@ -55,7 +55,8 @@ static int get_trace_fd(int *need_close) { char *trace = getenv("GIT_TRACE"); - if (!trace || !strcmp(trace, "0") || !strcasecmp(trace, "false")) + if (!trace || !strcmp(trace, "") || + !strcmp(trace, "0") || !strcasecmp(trace, "false")) return 0; if (!strcmp(trace, "1") || !strcasecmp(trace, "true")) return STDERR_FILENO; From f7197dff15a3ab12ce59626865c5a388a3bb0976 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 14 Oct 2006 15:48:35 -0700 Subject: [PATCH 075/114] git-svn: reduce memory usage for large commits apply_textdelta and send_stream can use a separate pool from the rest of the editor interface, so we'll use a separate SVN::Pool for them and clear the pool after each file is sent to SVN. This drastically reduces memory usage per-changeset committed, and makes large commits (and initial imports) of several thousand files possible. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-svn.perl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 0f968c8cea..54d2356933 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3354,9 +3354,11 @@ sub chg_file { seek $fh, 0, 0 or croak $!; my $exp = $md5->hexdigest; - my $atd = $self->apply_textdelta($fbat, undef, $self->{pool}); - my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool}); + my $pool = SVN::Pool->new; + my $atd = $self->apply_textdelta($fbat, undef, $pool); + my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); + $pool->clear; close $fh or croak $!; } From 0a7a9a12d65ca75601f841c93222652f41de09c0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 11 Oct 2006 00:20:43 +0200 Subject: [PATCH 076/114] cvsserver: Show correct letters for modified, removed and added files Earlier, cvsserver showed always an 'U', sometimes even without a space between the 'U' and the name. Now, the correct letter is shown, with a space. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 63 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 2130d57020..4de50d029d 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -805,7 +805,14 @@ sub req_update $meta = $updater->getmeta($filename); } - next unless ( $meta->{revision} ); + if ( ! defined $meta ) + { + $meta = { + name => $filename, + revision => 0, + filehash => 'added' + }; + } my $oldmeta = $meta; @@ -835,7 +842,7 @@ sub req_update and not exists ( $state->{opt}{C} ) ) { $log->info("Tell the client the file is modified"); - print "MT text U\n"; + print "MT text M \n"; print "MT fname $filename\n"; print "MT newline\n"; next; @@ -855,15 +862,36 @@ sub req_update } } elsif ( not defined ( $state->{entries}{$filename}{modified_hash} ) - or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} ) + or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} + or $meta->{filehash} eq 'added' ) { - $log->info("Updating '$filename'"); - # normal update, just send the new revision (either U=Update, or A=Add, or R=Remove) - print "MT +updated\n"; - print "MT text U\n"; - print "MT fname $filename\n"; - print "MT newline\n"; - print "MT -updated\n"; + # normal update, just send the new revision (either U=Update, + # or A=Add, or R=Remove) + if ( defined($wrev) && $wrev < 0 ) + { + $log->info("Tell the client the file is scheduled for removal"); + print "MT text R \n"; + print "MT fname $filename\n"; + print "MT newline\n"; + next; + } + elsif ( !defined($wrev) || $wrev == 0 ) + { + $log->info("Tell the client the file will be added"); + print "MT text A \n"; + print "MT fname $filename\n"; + print "MT newline\n"; + next; + + } + else { + $log->info("Updating '$filename' $wrev"); + print "MT +updated\n"; + print "MT text U \n"; + print "MT fname $filename\n"; + print "MT newline\n"; + print "MT -updated\n"; + } my ( $filepart, $dirpart ) = filenamesplit($filename,1); @@ -1709,6 +1737,17 @@ sub argsfromdir return if ( scalar ( @{$state->{args}} ) > 1 ); + my @gethead = @{$updater->gethead}; + + # push added files + foreach my $file (keys %{$state->{entries}}) { + if ( exists $state->{entries}{$file}{revision} && + $state->{entries}{$file}{revision} == 0 ) + { + push @gethead, { name => $file, filehash => 'added' }; + } + } + if ( scalar(@{$state->{args}}) == 1 ) { my $arg = $state->{args}[0]; @@ -1716,7 +1755,7 @@ sub argsfromdir $log->info("Only one arg specified, checking for directory expansion on '$arg'"); - foreach my $file ( @{$updater->gethead} ) + foreach my $file ( @gethead ) { next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) ); next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg ); @@ -1729,7 +1768,7 @@ sub argsfromdir $state->{args} = []; - foreach my $file ( @{$updater->gethead} ) + foreach my $file ( @gethead ) { next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) ); next unless ( $file->{name} =~ s/^$state->{prependdir}// ); From d988b82232bb8f5826a1619fd4dcba1a5a330f27 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 11 Oct 2006 00:33:28 +0200 Subject: [PATCH 077/114] cvsserver: fix "cvs diff" in a subdirectory Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 4de50d029d..08ad831a39 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -275,7 +275,7 @@ sub req_Directory $state->{directory} = "" if ( $state->{directory} eq "." ); $state->{directory} .= "/" if ( $state->{directory} =~ /\S/ ); - if ( not defined($state->{prependdir}) and $state->{localdir} eq "." and $state->{path} =~ /\S/ ) + if ( (not defined($state->{prependdir}) or $state->{prependdir} eq '') and $state->{localdir} eq "." and $state->{path} =~ /\S/ ) { $log->info("Setting prepend to '$state->{path}'"); $state->{prependdir} = $state->{path}; From ced78b3907dd60d8289ad5385ffcfd3339149957 Mon Sep 17 00:00:00 2001 From: Yasushi SHOJI Date: Sat, 14 Oct 2006 21:02:51 +0900 Subject: [PATCH 078/114] clone: the given repository dir should be relative to $PWD the repository argument for git-clone should be relative to $PWD instead of the given target directory. The old behavior gave us surprising success and you need a few minute to know why it worked. GIT_DIR is already exported so no need to cd into $D. And this makes $PWD for git-fetch-pack, which is the actual command to take the given repository dir, the same as git-clone. Signed-off-by: Yasushi SHOJI Signed-off-by: Junio C Hamano --- git-clone.sh | 2 +- t/t5600-clone-fail-cleanup.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/git-clone.sh b/git-clone.sh index 3998c55cef..bf54a11508 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -312,7 +312,7 @@ yes,yes) fi ;; *) - cd "$D" && case "$upload_pack" in + case "$upload_pack" in '') git-fetch-pack --all -k $quiet "$repo" ;; *) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;; esac >"$GIT_DIR/CLONE_HEAD" || { diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh index 0c6a363be9..041be04f5c 100755 --- a/t/t5600-clone-fail-cleanup.sh +++ b/t/t5600-clone-fail-cleanup.sh @@ -25,6 +25,12 @@ test_create_repo foo # clone doesn't like it if there is no HEAD. Is that a bug? (cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1) +# source repository given to git-clone should be relative to the +# current path not to the target dir +test_expect_failure \ + 'clone of non-existent (relative to $PWD) source should fail' \ + 'git-clone ../foo baz' + test_expect_success \ 'clone should work now that source exists' \ 'git-clone foo bar' From 29f049a0c277be72637f74f1f90a89dccd3475bc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 14 Oct 2006 23:37:41 -0700 Subject: [PATCH 079/114] Revert "move pack creation to version 3" This reverts commit 16854571aae6302f457c5fbee41ac64669b09595. Git as recent as v1.1.6 do not understand version 3 delta. v1.2.0 is Ok and I personally would say it is old enough, but the improvement between version 2 and version 3 delta is not bit enough to justify breaking older clients. We should resurrect this later, but when we do so we shold make it conditional. Signed-off-by: Junio C Hamano --- diff-delta.c | 8 ++------ pack.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/diff-delta.c b/diff-delta.c index 51df4608a8..fa16d06c8d 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -308,8 +308,8 @@ create_delta(const struct delta_index *index, continue; if (ref_size > top - src) ref_size = top - src; - if (ref_size > 0xffffff) - ref_size = 0xffffff; + if (ref_size > 0x10000) + ref_size = 0x10000; if (ref_size <= msize) break; while (ref_size-- && *src++ == *ref) @@ -318,8 +318,6 @@ create_delta(const struct delta_index *index, /* this is our best match so far */ msize = ref - entry->ptr; moff = entry->ptr - ref_data; - if (msize >= 0x10000) - break; /* this is good enough */ } } @@ -383,8 +381,6 @@ create_delta(const struct delta_index *index, if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; } msize >>= 8; if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; } - msize >>= 8; - if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; } *op = i; } diff --git a/pack.h b/pack.h index 05557da152..eb07b033ae 100644 --- a/pack.h +++ b/pack.h @@ -7,7 +7,7 @@ * Packed object header */ #define PACK_SIGNATURE 0x5041434b /* "PACK" */ -#define PACK_VERSION 3 +#define PACK_VERSION 2 #define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) struct pack_header { unsigned int hdr_signature; From 972a9155832165ea38febba2303e7c760050b5d8 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 15 Oct 2006 14:02:03 +0200 Subject: [PATCH 080/114] Make write_sha1_file_prepare() void Move file name generation from write_sha1_file_prepare() to the one caller that cares and make it a void function. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- sha1_file.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index d111be74a3..66cc7679ce 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1347,9 +1347,9 @@ void *read_object_with_reference(const unsigned char *sha1, } } -static char *write_sha1_file_prepare(void *buf, unsigned long len, - const char *type, unsigned char *sha1, - unsigned char *hdr, int *hdrlen) +static void write_sha1_file_prepare(void *buf, unsigned long len, + const char *type, unsigned char *sha1, + unsigned char *hdr, int *hdrlen) { SHA_CTX c; @@ -1361,8 +1361,6 @@ static char *write_sha1_file_prepare(void *buf, unsigned long len, SHA1_Update(&c, hdr, *hdrlen); SHA1_Update(&c, buf, len); SHA1_Final(sha1, &c); - - return sha1_file_name(sha1); } /* @@ -1521,7 +1519,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha /* Normally if we have it in the pack then we do not bother writing * it out into .git/objects/??/?{38} file. */ - filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); + write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); + filename = sha1_file_name(sha1); if (returnsha1) hashcpy(returnsha1, sha1); if (has_sha1_file(sha1)) From 7cfb5f367e50402c0bc75d89542d7ada4c5b8da4 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 15 Oct 2006 14:02:18 +0200 Subject: [PATCH 081/114] Replace open-coded version of hash_sha1_file() Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- sha1_file.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 66cc7679ce..716aef33e3 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -671,14 +671,8 @@ static void reprepare_packed_git(void) int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) { - char header[100]; unsigned char real_sha1[20]; - SHA_CTX c; - - SHA1_Init(&c); - SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size)); - SHA1_Update(&c, map, size); - SHA1_Final(real_sha1, &c); + hash_sha1_file(map, size, type, real_sha1); return hashcmp(sha1, real_sha1) ? -1 : 0; } From b32db4d0faec35ad8e6af35c54c37036b3d6c02f Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Mon, 16 Oct 2006 03:00:37 +0200 Subject: [PATCH 082/114] svnimport: Fix broken tags being generated Currently git-svnimport generates broken tags missing the timespec in the 'tagger' line. This is a random stab at a minimal fix. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-svnimport.perl b/git-svnimport.perl index 988514e293..aca0e4f64e 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -838,7 +838,7 @@ sub commit { print $out ("object $cid\n". "type commit\n". "tag $dest\n". - "tagger $committer_name <$committer_email>\n") and + "tagger $committer_name <$committer_email> 0 +0000\n") and close($out) or die "Cannot create tag object $dest: $!\n"; From 9de0834663f28bee9e6b2c4647ed6128241ed26f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 16 Oct 2006 19:58:54 -0700 Subject: [PATCH 083/114] Fix hash function in xdiff library Jim Mayering noticed that xdiff library took insanely long time when comparing files with many identical lines. This was because the hash function used in the library is broken on 64-bit architectures and caused too many collisions. http://thread.gmane.org/gmane.comp.version-control.git/28962/focus=28994 Acked-by: Davide Libenzi Signed-off-by: Junio C Hamano --- xdiff/xmacros.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xdiff/xmacros.h b/xdiff/xmacros.h index 4c2fde80c1..e2cd2023b3 100644 --- a/xdiff/xmacros.h +++ b/xdiff/xmacros.h @@ -24,14 +24,15 @@ #define XMACROS_H -#define GR_PRIME 0x9e370001UL #define XDL_MIN(a, b) ((a) < (b) ? (a): (b)) #define XDL_MAX(a, b) ((a) > (b) ? (a): (b)) #define XDL_ABS(v) ((v) >= 0 ? (v): -(v)) #define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9') -#define XDL_HASHLONG(v, b) (((unsigned long)(v) * GR_PRIME) >> ((CHAR_BIT * sizeof(unsigned long)) - (b))) +#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b))) +#define XDL_MASKBITS(b) ((1UL << (b)) - 1) +#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b)) #define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0) #define XDL_LE32_PUT(p, v) \ do { \ From 9b709e47ae46079efe1d7858580dcf5b8cf6ea33 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Mon, 16 Oct 2006 02:59:25 +0200 Subject: [PATCH 084/114] bisect reset: Leave the tree in usable state if git-checkout failed I had local modifications in the tree and doing bisect reset required me to manually edit .git/HEAD. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- git-bisect.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 06a8d26945..6da31e87a0 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -179,11 +179,12 @@ bisect_reset() { *) usage ;; esac - git checkout "$branch" && - rm -fr "$GIT_DIR/refs/bisect" - rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name" - rm -f "$GIT_DIR/BISECT_LOG" - rm -f "$GIT_DIR/BISECT_NAMES" + if git checkout "$branch"; then + rm -fr "$GIT_DIR/refs/bisect" + rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name" + rm -f "$GIT_DIR/BISECT_LOG" + rm -f "$GIT_DIR/BISECT_NAMES" + fi } bisect_replay () { From 17b96be29afd71577374e9deee663120b18eb8f1 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Tue, 17 Oct 2006 19:08:08 +0100 Subject: [PATCH 085/114] add proper dependancies on the xdiff source We are not rebuilding the xdiff library when its header files change. Add dependancies for those to the main Makefile. Signed-off-by: Andy Whitcroft Acked-by: Ryan Anderson Signed-off-by: Junio C Hamano --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 2c7c33855e..66c8b4b127 100644 --- a/Makefile +++ b/Makefile @@ -760,6 +760,8 @@ $(LIB_FILE): $(LIB_OBJS) rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o +$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ + xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h $(XDIFF_LIB): $(XDIFF_OBJS) rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) From e0b0830726286287744cc9e1a629a534bbe75452 Mon Sep 17 00:00:00 2001 From: Markus Amsler Date: Fri, 13 Oct 2006 00:19:35 +0200 Subject: [PATCH 086/114] git-imap-send: Strip smtp From_ header from imap message. Cyrus imap refuses messages with a 'From ' Header. [jc: Mike McCormack says this is fine with Courier as well.] Signed-off-by: Markus Amsler Signed-off-by: Junio C Hamano --- imap-send.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/imap-send.c b/imap-send.c index 362e474374..16804ab286 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1226,6 +1226,14 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs ) if (msg->len < 5 || strncmp( data, "From ", 5 )) return 0; + p = strchr( data, '\n' ); + if (p) { + p = &p[1]; + msg->len -= p-data; + *ofs += p-data; + data = p; + } + p = strstr( data, "\nFrom " ); if (p) msg->len = &p[1] - data; From 3c552873c698117689af4e5159c7e491fe3a89a3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 17 Oct 2006 16:23:26 -0400 Subject: [PATCH 087/114] index-pack: compare only the first 20-bytes of the key. The "union delta_base" is a strange beast. It is a 20-byte binary blob key to search a binary searchable deltas[] array, each element of which uses it to represent its base object with either a full 20-byte SHA-1 or an offset in the pack. Which representation is used is determined by another field of the deltas[] array element, obj->type, so there is no room for confusion, as long as we make sure we compare the keys for the same type only with appropriate length. The code compared the full union with memcmp(). When storing the in-pack offset, the union was first cleared before storing an unsigned long, so comparison worked fine. On 64-bit architectures, however, the union typically is 24-byte long; the code did not clear the remaining 4-byte alignment padding when storing a full 20-byte SHA-1 representation. Using memcmp() to compare the whole union was wrong. This fixes the comparison to look at the first 20-bytes of the union, regardless of the architecture. As long as ulong is smaller than 20-bytes this works fine. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/index-pack.c b/index-pack.c index fffddd25c9..56c590e3fa 100644 --- a/index-pack.c +++ b/index-pack.c @@ -23,6 +23,12 @@ union delta_base { unsigned long offset; }; +/* + * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want + * to memcmp() only the first 20 bytes. + */ +#define UNION_BASE_SZ 20 + struct delta_entry { struct object_entry *obj; @@ -211,7 +217,7 @@ static int find_delta(const union delta_base *base) struct delta_entry *delta = &deltas[next]; int cmp; - cmp = memcmp(base, &delta->base, sizeof(*base)); + cmp = memcmp(base, &delta->base, UNION_BASE_SZ); if (!cmp) return next; if (cmp < 0) { @@ -232,9 +238,9 @@ static int find_delta_childs(const union delta_base *base, if (first < 0) return -1; - while (first > 0 && !memcmp(&deltas[first - 1].base, base, sizeof(*base))) + while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ)) --first; - while (last < end && !memcmp(&deltas[last + 1].base, base, sizeof(*base))) + while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ)) ++last; *first_index = first; *last_index = last; @@ -312,7 +318,7 @@ static int compare_delta_entry(const void *a, const void *b) { const struct delta_entry *delta_a = a; const struct delta_entry *delta_b = b; - return memcmp(&delta_a->base, &delta_b->base, sizeof(union delta_base)); + return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ); } static void parse_pack_objects(void) From 1a3b55c6b424904835ebfd74c992a5bffbaa7e7e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 18 Oct 2006 15:56:22 -0400 Subject: [PATCH 088/114] reduce delta head inflated size Supposing that both the base and result sizes were both full size 64-bit values, their encoding would occupy only 9.2 bytes each. Therefore inflating 64 bytes is way overkill. Limit it to 20 bytes instead which should be plenty enough for a couple years to come. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- sha1_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_file.c b/sha1_file.c index 716aef33e3..47e2a29abd 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -908,7 +908,7 @@ static int packed_delta_info(struct packed_git *p, if (sizep) { const unsigned char *data; - unsigned char delta_head[64]; + unsigned char delta_head[20]; unsigned long result_size; z_stream stream; int st; From 8a83157e04e8f9654b3573cf04276895b1cbd68a Mon Sep 17 00:00:00 2001 From: "pclouds@gmail.com" Date: Thu, 19 Oct 2006 08:34:41 +0700 Subject: [PATCH 089/114] Reject hexstring longer than 40-bytes in get_short_sha1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Such a string can never be a valid object name. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- sha1_name.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sha1_name.c b/sha1_name.c index 9b226e3579..6ffee22081 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -157,7 +157,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1, char canonical[40]; unsigned char res[20]; - if (len < MINIMUM_ABBREV) + if (len < MINIMUM_ABBREV || len > 40) return -1; hashclr(res); memset(canonical, 'x', 40); From 6b09c7883f50044a68d93ef6872486bad2e93a9d Mon Sep 17 00:00:00 2001 From: "pclouds@gmail.com" Date: Thu, 19 Oct 2006 10:04:55 +0700 Subject: [PATCH 090/114] Add revspec documentation for ':path', ':[0-3]:path' and git-describe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- Documentation/git-rev-parse.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 2f1306c1d9..5d4257062d 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -111,7 +111,9 @@ SPECIFYING REVISIONS A revision parameter typically, but not necessarily, names a commit object. They use what is called an 'extended SHA1' -syntax. +syntax. Here are various ways to spell object names. The +ones listed near the end of this list are to name trees and +blobs contained in a commit. * The full SHA1 object name (40-byte hexadecimal string), or a substring of such that is unique within the repository. @@ -119,6 +121,9 @@ syntax. name the same commit object if there are no other object in your repository whose object name starts with dae86e. +* An output from `git-describe`; i.e. a closest tag, followed by a + dash, a 'g', and an abbreviated object name. + * A symbolic ref name. E.g. 'master' typically means the commit object referenced by $GIT_DIR/refs/heads/master. If you happen to have both heads/master and tags/master, you can @@ -156,6 +161,15 @@ syntax. and dereference the tag recursively until a non-tag object is found. +* A suffix ':' followed by a path; this names the blob or tree + at the given path in the tree-ish object named by the part + before the colon. + +* A colon, optionally followed by a stage number (0 to 3) and a + colon, followed by a path; this names a blob object in the + index at the given path. Missing stage number (and the colon + that follows it) names an stage 0 entry. + Here is an illustration, by Jon Loeliger. Both node B and C are a commit parents of commit node A. Parent commits are ordered left-to-right. From 7768e27e1d3f3d5e253e795433033b5de1d1c157 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 19 Oct 2006 10:33:01 +0200 Subject: [PATCH 091/114] Don't use $author_name undefined when $from contains no /\serr new-file OK. Log says: Date: Thu, 19 Oct 2006 10:26:24 +0200 Sendmail: /usr/sbin/sendmail From: j Subject: Cc: To: k Result: OK $ cat err Use of uninitialized value in pattern match (m//) at /p/bin/git-send-email line 416. Use of uninitialized value in concatenation (.) or string at /p/bin/git-send-email line 420. Use of uninitialized value in concatenation (.) or string at /p/bin/git-send-email line 468. There's a patch for the $author_name part below. The example above shows that $subject may also be used uninitialized. That should be easy to fix, too. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index b17d261987..1c6d2cc787 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -412,7 +412,7 @@ sub send_message } my ($author_name) = ($from =~ /^(.*?)\s+ Date: Thu, 19 Oct 2006 19:26:08 -0700 Subject: [PATCH 092/114] git-apply: prepare for upcoming GNU diff -u format change. The latest GNU diff from CVS emits an empty line to express an empty context line, instead of more traditional "single white space followed by a newline". Do not get broken by it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-apply.c | 9 ++++++ t/t4118-apply-empty-context.sh | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100755 t/t4118-apply-empty-context.sh diff --git a/builtin-apply.c b/builtin-apply.c index cbe597771b..11a5277a69 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -934,6 +934,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s switch (*line) { default: return -1; + case '\n': /* newer GNU diff, an empty context line */ case ' ': oldlines--; newlines--; @@ -1623,6 +1624,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i first = '-'; } switch (first) { + case '\n': + /* Newer GNU diff, empty context line */ + if (plen < 0) + /* ... followed by '\No newline'; nothing */ + break; + old[oldsize++] = '\n'; + new[newsize++] = '\n'; + break; case ' ': case '-': memcpy(old + oldsize, patch + 1, plen); diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh new file mode 100755 index 0000000000..7309422fe5 --- /dev/null +++ b/t/t4118-apply-empty-context.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Copyright (c) 2006 Junio C Hamano +# + +test_description='git-apply with new style GNU diff with empty context + +' + +. ./test-lib.sh + +test_expect_success setup ' + { + echo; echo; + echo A; echo B; echo C; + echo; + } >file1 && + cat file1 >file1.orig && + { + cat file1 && + echo Q | tr -d "\\012" + } >file2 && + cat file2 >file2.orig + git add file1 file2 && + sed -e "/^B/d" file1 && + sed -e "/^B/d" file2 && + cat file1 >file1.mods && + cat file2 >file2.mods && + git diff | + sed -e "s/^ \$//" >diff.output +' + +test_expect_success 'apply --numstat' ' + + git apply --numstat diff.output >actual && + { + echo "0 1 file1" && + echo "0 1 file2" + } >expect && + diff -u expect actual + +' + +test_expect_success 'apply --apply' ' + + cat file1.orig >file1 && + cat file2.orig >file2 && + git update-index file1 file2 && + git apply --index diff.output && + diff -u file1.mods file1 && + diff -u file2.mods file2 +' + +test_done + From 96a035d1db9082d244867033020d0ceb571cf94e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 20 Oct 2006 16:37:49 -0700 Subject: [PATCH 093/114] pager: default to LESS=FRS Recent change to paginate "git diff" by default is often irritating when you do not have any change (or very small change) in your working tree. Signed-off-by: Junio C Hamano --- pager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pager.c b/pager.c index dcb398da8e..8bd33a15ca 100644 --- a/pager.c +++ b/pager.c @@ -50,7 +50,7 @@ void setup_pager(void) close(fd[0]); close(fd[1]); - setenv("LESS", "-RS", 0); + setenv("LESS", "FRS", 0); run_pager(pager); die("unable to execute pager '%s'", pager); exit(255); From 0b92f1a9d213644d2cd6c1870091c090a6b7eca9 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Fri, 20 Oct 2006 23:24:32 +0200 Subject: [PATCH 094/114] Fix typo in show-index.c Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- show-index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/show-index.c b/show-index.c index c21d660b62..a30a2de5d1 100644 --- a/show-index.c +++ b/show-index.c @@ -8,7 +8,7 @@ int main(int argc, char **argv) static unsigned int top_index[256]; if (fread(top_index, sizeof(top_index), 1, stdin) != 1) - die("unable to read idex"); + die("unable to read index"); nr = 0; for (i = 0; i < 256; i++) { unsigned n = ntohl(top_index[i]); From 87b787ac77e0d8f81468dcba2a39c9d0287870c3 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Fri, 20 Oct 2006 23:38:31 +0400 Subject: [PATCH 095/114] git-clone: define die() and use it. Signed-off-by: Dmitry V. Levin Signed-off-by: Junio C Hamano --- git-clone.sh | 61 +++++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/git-clone.sh b/git-clone.sh index bf54a11508..786d65a859 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -8,11 +8,15 @@ # See git-sh-setup why. unset CDPATH -usage() { - echo >&2 "Usage: $0 [--template=] [--use-separate-remote] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [-n] []" +die() { + echo >&2 "$@" exit 1 } +usage() { + die "Usage: $0 [--template=] [--use-separate-remote] [--reference ] [--bare] [-l [-s]] [-q] [-u ] [--origin ] [-n] []" +} + get_repo_base() { (cd "$1" && (cd .git ; pwd)) 2> /dev/null } @@ -35,11 +39,9 @@ clone_dumb_http () { "`git-repo-config --bool http.noEPSV`" = true ]; then curl_extra_args="${curl_extra_args} --disable-epsv" fi - http_fetch "$1/info/refs" "$clone_tmp/refs" || { - echo >&2 "Cannot get remote repository information. + http_fetch "$1/info/refs" "$clone_tmp/refs" || + die "Cannot get remote repository information. Perhaps git-update-server-info needs to be run there?" - exit 1; - } while read sha1 refname do name=`expr "z$refname" : 'zrefs/\(.*\)'` && @@ -143,17 +145,12 @@ while '') usage ;; */*) - echo >&2 "'$2' is not suitable for an origin name" - exit 1 + die "'$2' is not suitable for an origin name" esac - git-check-ref-format "heads/$2" || { - echo >&2 "'$2' is not suitable for a branch name" - exit 1 - } - test -z "$origin_override" || { - echo >&2 "Do not give more than one --origin options." - exit 1 - } + git-check-ref-format "heads/$2" || + die "'$2' is not suitable for a branch name" + test -z "$origin_override" || + die "Do not give more than one --origin options." origin_override=yes origin="$2"; shift ;; @@ -169,24 +166,19 @@ do done repo="$1" -if test -z "$repo" -then - echo >&2 'you must specify a repository to clone.' - exit 1 -fi +test -n "$repo" || + die 'you must specify a repository to clone.' # --bare implies --no-checkout if test yes = "$bare" then if test yes = "$origin_override" then - echo >&2 '--bare and --origin $origin options are incompatible.' - exit 1 + die '--bare and --origin $origin options are incompatible.' fi if test t = "$use_separate_remote" then - echo >&2 '--bare and --use-separate-remote options are incompatible.' - exit 1 + die '--bare and --use-separate-remote options are incompatible.' fi no_checkout=yes fi @@ -206,7 +198,7 @@ fi dir="$2" # Try using "humanish" part of source repo if user didn't specify one [ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g') -[ -e "$dir" ] && echo "$dir already exists." && usage +[ -e "$dir" ] && die "destination directory '$dir' already exists." mkdir -p "$dir" && D=$(cd "$dir" && pwd) && trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0 @@ -233,7 +225,7 @@ then cd reference-tmp && tar xf -) else - echo >&2 "$reference: not a local directory." && usage + die "reference repository '$reference' is not a local directory." fi fi @@ -242,10 +234,8 @@ rm -f "$GIT_DIR/CLONE_HEAD" # We do local magic only when the user tells us to. case "$local,$use_local" in yes,yes) - ( cd "$repo/objects" ) || { - echo >&2 "-l flag seen but $repo is not local." - exit 1 - } + ( cd "$repo/objects" ) || + die "-l flag seen but repository '$repo' is not local." case "$local_shared" in no) @@ -307,18 +297,15 @@ yes,yes) then clone_dumb_http "$repo" "$D" else - echo >&2 "http transport not supported, rebuild Git with curl support" - exit 1 + die "http transport not supported, rebuild Git with curl support" fi ;; *) case "$upload_pack" in '') git-fetch-pack --all -k $quiet "$repo" ;; *) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;; - esac >"$GIT_DIR/CLONE_HEAD" || { - echo >&2 "fetch-pack from '$repo' failed." - exit 1 - } + esac >"$GIT_DIR/CLONE_HEAD" || + die "fetch-pack from '$repo' failed." ;; esac ;; From 2d477051ef260aad352d63fc7d9c07e4ebb4359b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 20 Oct 2006 14:45:21 -0400 Subject: [PATCH 096/114] add the capability for index-pack to read from a stream This patch only adds the streaming capability to index-pack. Although the code is different it has the exact same functionality as before to make sure nothing broke. This is in preparation for receiving packs over the net, parse them on the fly, fix them up if they are "thin" packs, and keep the resulting pack instead of exploding it into loose objects. But such functionality should come separately. One immediate advantage of this patch is that index-pack can now deal with packs up to 4GB in size even on 32-bit architectures since the pack is not entirely mmap()'d all at once anymore. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- index-pack.c | 244 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 101 deletions(-) diff --git a/index-pack.c b/index-pack.c index 56c590e3fa..e33f60524f 100644 --- a/index-pack.c +++ b/index-pack.c @@ -13,6 +13,8 @@ static const char index_pack_usage[] = struct object_entry { unsigned long offset; + unsigned long size; + unsigned int hdr_size; enum object_type type; enum object_type real_type; unsigned char sha1[20]; @@ -36,51 +38,68 @@ struct delta_entry }; static const char *pack_name; -static unsigned char *pack_base; -static unsigned long pack_size; static struct object_entry *objects; static struct delta_entry *deltas; static int nr_objects; static int nr_deltas; +/* We always read in 4kB chunks. */ +static unsigned char input_buffer[4096]; +static unsigned long input_offset, input_len, consumed_bytes; +static SHA_CTX input_ctx; +static int input_fd; + +/* + * Make sure at least "min" bytes are available in the buffer, and + * return the pointer to the buffer. + */ +static void * fill(int min) +{ + if (min <= input_len) + return input_buffer + input_offset; + if (min > sizeof(input_buffer)) + die("cannot fill %d bytes", min); + if (input_offset) { + SHA1_Update(&input_ctx, input_buffer, input_offset); + memcpy(input_buffer, input_buffer + input_offset, input_len); + input_offset = 0; + } + do { + int ret = xread(input_fd, input_buffer + input_len, + sizeof(input_buffer) - input_len); + if (ret <= 0) { + if (!ret) + die("early EOF"); + die("read error on input: %s", strerror(errno)); + } + input_len += ret; + } while (input_len < min); + return input_buffer; +} + +static void use(int bytes) +{ + if (bytes > input_len) + die("used more bytes than were available"); + input_len -= bytes; + input_offset += bytes; + consumed_bytes += bytes; +} + static void open_pack_file(void) { - int fd; - struct stat st; - - fd = open(pack_name, O_RDONLY); - if (fd < 0) + input_fd = open(pack_name, O_RDONLY); + if (input_fd < 0) die("cannot open packfile '%s': %s", pack_name, strerror(errno)); - if (fstat(fd, &st)) { - int err = errno; - close(fd); - die("cannot fstat packfile '%s': %s", pack_name, - strerror(err)); - } - pack_size = st.st_size; - pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (pack_base == MAP_FAILED) { - int err = errno; - close(fd); - die("cannot mmap packfile '%s': %s", pack_name, - strerror(err)); - } - close(fd); + SHA1_Init(&input_ctx); } static void parse_pack_header(void) { - const struct pack_header *hdr; - unsigned char sha1[20]; - SHA_CTX ctx; - - /* Ensure there are enough bytes for the header and final SHA1 */ - if (pack_size < sizeof(struct pack_header) + 20) - die("packfile '%s' is too small", pack_name); + struct pack_header *hdr = fill(sizeof(struct pack_header)); /* Header consistency check */ - hdr = (void *)pack_base; if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) die("packfile '%s' signature mismatch", pack_name); if (!pack_version_ok(hdr->hdr_version)) @@ -88,13 +107,8 @@ static void parse_pack_header(void) pack_name, ntohl(hdr->hdr_version)); nr_objects = ntohl(hdr->hdr_entries); - - /* Check packfile integrity */ - SHA1_Init(&ctx); - SHA1_Update(&ctx, pack_base, pack_size - 20); - SHA1_Final(sha1, &ctx); - if (hashcmp(sha1, pack_base + pack_size - 20)) - die("packfile '%s' SHA1 mismatch", pack_name); + use(sizeof(struct pack_header)); + /*fprintf(stderr, "Indexing %d objects\n", nr_objects);*/ } static void bad_object(unsigned long offset, const char *format, @@ -112,85 +126,78 @@ static void bad_object(unsigned long offset, const char *format, ...) pack_name, offset, buf); } -static void *unpack_entry_data(unsigned long offset, - unsigned long *current_pos, unsigned long size) +static void *unpack_entry_data(unsigned long offset, unsigned long size) { - unsigned long pack_limit = pack_size - 20; - unsigned long pos = *current_pos; z_stream stream; void *buf = xmalloc(size); memset(&stream, 0, sizeof(stream)); stream.next_out = buf; stream.avail_out = size; - stream.next_in = pack_base + pos; - stream.avail_in = pack_limit - pos; + stream.next_in = fill(1); + stream.avail_in = input_len; inflateInit(&stream); for (;;) { int ret = inflate(&stream, 0); - if (ret == Z_STREAM_END) + use(input_len - stream.avail_in); + if (stream.total_out == size && ret == Z_STREAM_END) break; if (ret != Z_OK) bad_object(offset, "inflate returned %d", ret); + stream.next_in = fill(1); + stream.avail_in = input_len; } inflateEnd(&stream); - if (stream.total_out != size) - bad_object(offset, "size mismatch (expected %lu, got %lu)", - size, stream.total_out); - *current_pos = pack_limit - stream.avail_in; return buf; } -static void *unpack_raw_entry(unsigned long offset, - enum object_type *obj_type, - unsigned long *obj_size, - union delta_base *delta_base, - unsigned long *next_obj_offset) +static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base) { - unsigned long pack_limit = pack_size - 20; - unsigned long pos = offset; - unsigned char c; + unsigned char *p, c; unsigned long size, base_offset; unsigned shift; - enum object_type type; - void *data; - c = pack_base[pos++]; - type = (c >> 4) & 7; + obj->offset = consumed_bytes; + + p = fill(1); + c = *p; + use(1); + obj->type = (c >> 4) & 7; size = (c & 15); shift = 4; while (c & 0x80) { - if (pos >= pack_limit) - bad_object(offset, "object extends past end of pack"); - c = pack_base[pos++]; + p = fill(1); + c = *p; + use(1); size += (c & 0x7fUL) << shift; shift += 7; } + obj->size = size; - switch (type) { + switch (obj->type) { case OBJ_REF_DELTA: - if (pos + 20 >= pack_limit) - bad_object(offset, "object extends past end of pack"); - hashcpy(delta_base->sha1, pack_base + pos); - pos += 20; + hashcpy(delta_base->sha1, fill(20)); + use(20); break; case OBJ_OFS_DELTA: memset(delta_base, 0, sizeof(*delta_base)); - c = pack_base[pos++]; + p = fill(1); + c = *p; + use(1); base_offset = c & 127; while (c & 128) { base_offset += 1; if (!base_offset || base_offset & ~(~0UL >> 7)) - bad_object(offset, "offset value overflow for delta base object"); - if (pos >= pack_limit) - bad_object(offset, "object extends past end of pack"); - c = pack_base[pos++]; + bad_object(obj->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 = offset - base_offset; - if (delta_base->offset >= offset) - bad_object(offset, "delta base offset is out of bound"); + delta_base->offset = obj->offset - base_offset; + if (delta_base->offset >= obj->offset) + bad_object(obj->offset, "delta base offset is out of bound"); break; case OBJ_COMMIT: case OBJ_TREE: @@ -198,13 +205,38 @@ static void *unpack_raw_entry(unsigned long offset, case OBJ_TAG: break; default: - bad_object(offset, "bad object type %d", type); + bad_object(obj->offset, "bad object type %d", obj->type); } + obj->hdr_size = consumed_bytes - obj->offset; - data = unpack_entry_data(offset, &pos, size); - *obj_type = type; - *obj_size = size; - *next_obj_offset = pos; + return unpack_entry_data(obj->offset, obj->size); +} + +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 pg_offset = from % getpagesize(); + unsigned char *map, *data; + z_stream stream; + int st; + + map = mmap(NULL, len + pg_offset, PROT_READ, MAP_PRIVATE, + input_fd, from - pg_offset); + if (map == MAP_FAILED) + die("cannot mmap packfile '%s': %s", pack_name, strerror(errno)); + data = xmalloc(obj->size); + memset(&stream, 0, sizeof(stream)); + stream.next_out = data; + stream.avail_out = obj->size; + stream.next_in = map + pg_offset; + stream.avail_in = len; + inflateInit(&stream); + while ((st = inflate(&stream, Z_FINISH)) == Z_OK); + inflateEnd(&stream); + if (st != Z_STREAM_END || stream.total_out != obj->size) + die("serious inflate inconsistency"); + munmap(map, len + pg_offset); return data; } @@ -280,15 +312,12 @@ static void resolve_delta(struct delta_entry *delta, void *base_data, unsigned long delta_size; void *result; unsigned long result_size; - enum object_type delta_type; union delta_base delta_base; - unsigned long next_obj_offset; int j, first, last; obj->real_type = type; - delta_data = unpack_raw_entry(obj->offset, &delta_type, - &delta_size, &delta_base, - &next_obj_offset); + delta_data = get_data_from_pack(obj); + delta_size = obj->size; result = patch_delta(base_data, base_size, delta_data, delta_size, &result_size); free(delta_data); @@ -321,13 +350,13 @@ static int compare_delta_entry(const void *a, const void *b) return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ); } -static void parse_pack_objects(void) +/* Parse all objects and return the pack content SHA1 hash */ +static void parse_pack_objects(unsigned char *sha1) { int i; - unsigned long offset = sizeof(struct pack_header); struct delta_entry *delta = deltas; void *data; - unsigned long data_size; + struct stat st; /* * First pass: @@ -337,19 +366,29 @@ static void parse_pack_objects(void) */ for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; - obj->offset = offset; - data = unpack_raw_entry(offset, &obj->type, &data_size, - &delta->base, &offset); + data = unpack_raw_entry(obj, &delta->base); obj->real_type = obj->type; if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) { nr_deltas++; delta->obj = obj; delta++; } else - sha1_object(data, data_size, obj->type, obj->sha1); + sha1_object(data, obj->size, obj->type, obj->sha1); free(data); } - if (offset != pack_size - 20) + objects[i].offset = consumed_bytes; + + /* Check pack integrity */ + SHA1_Update(&input_ctx, input_buffer, input_offset); + SHA1_Final(sha1, &input_ctx); + if (hashcmp(fill(20), sha1)) + die("packfile '%s' SHA1 mismatch", pack_name); + use(20); + + /* If input_fd is a file, we should have reached its end now. */ + if (fstat(input_fd, &st)) + die("cannot fstat packfile '%s': %s", pack_name, strerror(errno)); + if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes) die("packfile '%s' has junk at the end", pack_name); /* Sort deltas by base SHA1/offset for fast searching */ @@ -378,18 +417,17 @@ static void parse_pack_objects(void) ofs = !find_delta_childs(&base, &ofs_first, &ofs_last); if (!ref && !ofs) continue; - data = unpack_raw_entry(obj->offset, &obj->type, &data_size, - &base, &offset); + data = get_data_from_pack(obj); if (ref) for (j = ref_first; j <= ref_last; j++) if (deltas[j].obj->type == OBJ_REF_DELTA) resolve_delta(&deltas[j], data, - data_size, obj->type); + obj->size, obj->type); if (ofs) for (j = ofs_first; j <= ofs_last; j++) if (deltas[j].obj->type == OBJ_OFS_DELTA) resolve_delta(&deltas[j], data, - data_size, obj->type); + obj->size, obj->type); free(data); } @@ -408,6 +446,10 @@ static int sha1_compare(const void *_a, const void *_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 void write_index_file(const char *index_name, unsigned char *sha1) { struct sha1file *f; @@ -467,7 +509,7 @@ static void write_index_file(const char *index_name, unsigned char *sha1) sha1write(f, obj->sha1, 20); SHA1_Update(&ctx, obj->sha1, 20); } - sha1write(f, pack_base + pack_size - 20, 20); + sha1write(f, sha1, 20); sha1close(f, NULL, 1); free(sorted_by_sha); SHA1_Final(sha1, &ctx); @@ -513,9 +555,9 @@ int main(int argc, char **argv) open_pack_file(); parse_pack_header(); - objects = xcalloc(nr_objects, sizeof(struct object_entry)); + objects = xcalloc(nr_objects + 1, sizeof(struct object_entry)); deltas = xcalloc(nr_objects, sizeof(struct delta_entry)); - parse_pack_objects(); + parse_pack_objects(sha1); free(deltas); write_index_file(index_name, sha1); free(objects); From fc61e313da0a124e9f6a213cd76944a8c4e6c918 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sat, 21 Oct 2006 20:51:04 +0200 Subject: [PATCH 097/114] git-merge: show usage if run without arguments Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- git-merge.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-merge.sh b/git-merge.sh index 5b34b4de99..789f4de595 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -93,6 +93,8 @@ finish () { esac } +case "$#" in 0) usage ;; esac + rloga= while case "$#" in 0) break ;; esac do From 5ea0921cbeb248a6505c32601ad841f706f942fd Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Sun, 22 Oct 2006 13:30:24 +0200 Subject: [PATCH 098/114] Fix usagestring for git-branch Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- git-branch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-branch.sh b/git-branch.sh index 4f31903d63..f823c788fd 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -1,6 +1,6 @@ #!/bin/sh -USAGE='[-l] [(-d | -D) ] | [[-f] []] | -r' +USAGE='[-l] [-f] [] | (-d | -D) | [-r]' LONG_USAGE='If no arguments, show available branches and mark current branch with a star. If one argument, create a new branch based off of current HEAD. If two arguments, create a new branch based off of .' From e7fb022a42a83cbdb33e40f82ffca28eceb423c9 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 21 Oct 2006 17:52:19 +0200 Subject: [PATCH 099/114] gitweb: Whitespace cleanup - tabs are for indent, spaces are for align (2) Code should be aligned the same way, regardless of tab size. Use tabs for indent, but spaces for align. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0ec1eeffa1..d01ac9439a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1671,13 +1671,13 @@ sub git_print_tree_entry { if ($t->{'type'} eq "blob") { print "" . $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key), - -class => "list"}, esc_html($t->{'name'})) . "\n"; + file_name=>"$basedir$t->{'name'}", %base_key), + -class => "list"}, esc_html($t->{'name'})) . "\n"; print ""; if ($have_blame) { print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'}, - file_name=>"$basedir$t->{'name'}", %base_key)}, - "blame"); + file_name=>"$basedir$t->{'name'}", %base_key)}, + "blame"); } if (defined $hash_base) { if ($have_blame) { @@ -1689,8 +1689,8 @@ sub git_print_tree_entry { } print " | " . $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base, - file_name=>"$basedir$t->{'name'}")}, - "raw"); + file_name=>"$basedir$t->{'name'}")}, + "raw"); print "\n"; } elsif ($t->{'type'} eq "tree") { @@ -1758,7 +1758,7 @@ sub git_difftree_body { print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chng\n"; print ""; @@ -1785,11 +1785,11 @@ sub git_difftree_body { print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, - file_name=>$diff{'file'})}, - "blame") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed @@ -1810,8 +1810,8 @@ sub git_difftree_body { } print ""; print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, - hash_base=>$hash, file_name=>$diff{'file'}), - -class => "list"}, esc_html($diff{'file'})); + hash_base=>$hash, file_name=>$diff{'file'}), + -class => "list"}, esc_html($diff{'file'})); print "\n"; print "$mode_chnge\n"; print ""; @@ -1822,19 +1822,19 @@ sub git_difftree_body { print $cgi->a({-href => "#patch$patchno"}, "patch"); } else { print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'file'})}, - "diff"); + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'file'})}, + "diff"); } print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$hash, - file_name=>$diff{'file'})}, - "blame") . " | "; + file_name=>$diff{'file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$hash, - file_name=>$diff{'file'})}, - "history"); + file_name=>$diff{'file'})}, + "history"); print "\n"; } elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied @@ -1862,19 +1862,19 @@ sub git_difftree_body { print $cgi->a({-href => "#patch$patchno"}, "patch"); } else { print $cgi->a({-href => href(action=>"blobdiff", - hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, - hash_base=>$hash, hash_parent_base=>$parent, - file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, - "diff"); + hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'}, + hash_base=>$hash, hash_parent_base=>$parent, + file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})}, + "diff"); } print " | "; } print $cgi->a({-href => href(action=>"blame", hash_base=>$parent, - file_name=>$diff{'from_file'})}, - "blame") . " | "; + file_name=>$diff{'from_file'})}, + "blame") . " | "; print $cgi->a({-href => href(action=>"history", hash_base=>$parent, - file_name=>$diff{'from_file'})}, - "history"); + file_name=>$diff{'from_file'})}, + "history"); print "\n"; } # we should not encounter Unmerged (U) or Unknown (X) status @@ -2800,7 +2800,7 @@ sub git_tree { # FIXME: Should be available when we have no hash base as well. push @views_nav, $cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, - "snapshot"); + "snapshot"); } git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav)); git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base); From 300454feaba846b6bf95625c607d7096d81d26eb Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 21 Oct 2006 17:53:09 +0200 Subject: [PATCH 100/114] gitweb: Do not esc_html $basedir argument to git_print_tree_entry In git_tree, rename $base variable (which is passed as $basedir argument to git_print_tree_entry) to $basedir. Do not esc_html $basedir, as it is part of file_name ('f') argument in link and not printed. Add '/' at the end only if $basedir is not empty (it is empty for top directory) and doesn't end in '/' already. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index d01ac9439a..23b26a2db2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2783,7 +2783,7 @@ sub git_tree { my $refs = git_get_references(); my $ref = format_ref_marker($refs, $hash_base); git_header_html(); - my $base = ""; + my $basedir = ''; my ($have_blame) = gitweb_check_feature('blame'); if (defined $hash_base && (my %co = parse_commit($hash_base))) { my @views_nav = (); @@ -2811,7 +2811,10 @@ sub git_tree { print "
$hash
\n"; } if (defined $file_name) { - $base = esc_html("$file_name/"); + $basedir = $file_name; + if ($basedir ne '' && substr($basedir, -1) ne '/') { + $basedir .= '/'; + } } git_print_page_path($file_name, 'tree', $hash_base); print "
\n"; @@ -2827,7 +2830,7 @@ sub git_tree { } $alternate ^= 1; - git_print_tree_entry(\%t, $base, $hash_base, $have_blame); + git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame); print "\n"; } From 474a90fef9ebcdedee041b2def4b9a98b94cd146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Sun, 22 Oct 2006 20:46:36 +0200 Subject: [PATCH 101/114] git-vc: better installation instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide some more detailed installation instructions, for the elisp-challenged among us. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 4189c4ced0..80e767533a 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -23,7 +23,10 @@ ;; system. ;; ;; To install: put this file on the load-path and add GIT to the list -;; of supported backends in `vc-handled-backends'. +;; of supported backends in `vc-handled-backends'; the following line, +;; placed in your ~/.emacs, will accomplish this: +;; +;; (add-to-list 'vc-handled-backends 'GIT) ;; ;; TODO ;; - changelog generation From 2eb53e65bd9cdd3b76a6447a1a51dee6e5de96a3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 22 Oct 2006 19:01:23 -0400 Subject: [PATCH 102/114] Make prune also run prune-packed Both the git-prune manpage and everday.txt say that git-prune should also prune unpacked objects that are also found in packs, by running git prune-packed. Junio thought this was "a regression when prune was rewritten as a built-in." So modify prune to call prune-packed again. Signed-off-by: J. Bruce Fields --- builtin-prune-packed.c | 11 +++++------ builtin-prune.c | 2 ++ builtin.h | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 960db49859..e12b6cf599 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -4,9 +4,7 @@ static const char prune_packed_usage[] = "git-prune-packed [-n]"; -static int dryrun; - -static void prune_dir(int i, DIR *dir, char *pathname, int len) +static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun) { struct dirent *de; char hex[40]; @@ -31,7 +29,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len) rmdir(pathname); } -static void prune_packed_objects(void) +void prune_packed_objects(int dryrun) { int i; static char pathname[PATH_MAX]; @@ -50,7 +48,7 @@ static void prune_packed_objects(void) d = opendir(pathname); if (!d) continue; - prune_dir(i, d, pathname, len + 3); + prune_dir(i, d, pathname, len + 3, dryrun); closedir(d); } } @@ -58,6 +56,7 @@ static void prune_packed_objects(void) int cmd_prune_packed(int argc, const char **argv, const char *prefix) { int i; + int dryrun; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -73,6 +72,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix) usage(prune_packed_usage); } sync(); - prune_packed_objects(); + prune_packed_objects(dryrun); return 0; } diff --git a/builtin-prune.c b/builtin-prune.c index 6228c7907b..7290e6d9aa 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -255,5 +255,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) prune_object_dir(get_object_directory()); + sync(); + prune_packed_objects(show_only); return 0; } diff --git a/builtin.h b/builtin.h index f9fa9ff1d2..f71b9629b9 100644 --- a/builtin.h +++ b/builtin.h @@ -11,6 +11,7 @@ extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const cha extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip); extern void stripspace(FILE *in, FILE *out); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); +extern void prune_packed_objects(int); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); From 0abc0260fa3419de649fcc1444e3d256a17ca6c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 22 Oct 2006 20:28:10 -0700 Subject: [PATCH 103/114] pager: default to LESS=FRSX not LESS=FRS Signed-off-by: Junio C Hamano --- pager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pager.c b/pager.c index 8bd33a15ca..4587fbbdb5 100644 --- a/pager.c +++ b/pager.c @@ -50,7 +50,7 @@ void setup_pager(void) close(fd[0]); close(fd[1]); - setenv("LESS", "FRS", 0); + setenv("LESS", "FRSX", 0); run_pager(pager); die("unable to execute pager '%s'", pager); exit(255); From 4df118ed6041dc0f126d55231a6621be05882b5f Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 21 Oct 2006 17:53:55 +0200 Subject: [PATCH 104/114] gitweb: Improve git_print_page_path Add link to "tree root" (root directory) also for not defined name, for example for "tree" action without defined "file_name" which means "tree root". Add " / " at the end of path when $type eq "tree". Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 209b3180a2..126cf3c2e2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1615,17 +1615,16 @@ sub git_print_page_path { my $type = shift; my $hb = shift; - if (!defined $name) { - print "
/
\n"; - } else { + + print "
"; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), + -title => 'tree root'}, "[$project]"); + print " / "; + if (defined $name) { my @dirname = split '/', $name; my $basename = pop @dirname; my $fullname = ''; - print "
"; - print $cgi->a({-href => href(action=>"tree", hash_base=>$hb), - -title => 'tree root'}, "[$project]"); - print " / "; foreach my $dir (@dirname) { $fullname .= ($fullname ? '/' : '') . $dir; print $cgi->a({-href => href(action=>"tree", file_name=>$fullname, @@ -1641,11 +1640,12 @@ sub git_print_page_path { print $cgi->a({-href => href(action=>"tree", file_name=>$file_name, hash_base=>$hb), -title => $name}, esc_html($basename)); + print " / "; } else { print esc_html($basename); } - print "
\n"; } + print "
\n"; } # sub git_print_log (\@;%) { From b6b7fc7283bd091822541c0286340e78b0c497a2 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sat, 21 Oct 2006 17:54:44 +0200 Subject: [PATCH 105/114] gitweb: Add '..' (up directory) to tree view if applicable Adds '..' (up directory) link at the top of "tree" view listing, if both $hash_base and $file_name are provided, and $file_name is not empty string. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 126cf3c2e2..c9e57f0516 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2871,6 +2871,30 @@ sub git_tree { print "
\n"; print "\n"; my $alternate = 1; + # '..' (top directory) link if possible + if (defined $hash_base && + defined $file_name && $file_name =~ m![^/]+$!) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + my $up = $file_name; + $up =~ s!/?[^/]+$!!; + undef $up unless $up; + # based on git_print_tree_entry + print '\n"; + print '\n"; + print "\n"; + + print "\n"; + } foreach my $line (@entries) { my %t = parse_ls_tree_line($line, -z => 1); From 178e015c0543b581a40adbf4822f44fa592ff68b Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Mon, 23 Oct 2006 01:09:35 -0400 Subject: [PATCH 106/114] Use column indexes in git-cvsserver where necessary. Tonight I found a git-cvsserver instance spending a lot of time in disk IO while trying to process operations against a Git repository with >30,000 objects contained in it. Blowing away my SQLLite database and rebuilding all tables with indexes on the attributes that git-cvsserver frequently runs queries against seems to have resolved the issue quite nicely. Since the indexes shouldn't hurt performance on small repositories and always helps on larger repositories we should just always create them when creating the revision storage tables. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 08ad831a39..8817f8bb4f 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2118,9 +2118,17 @@ sub new mode TEXT NOT NULL ) "); + $self->{dbh}->do(" + CREATE INDEX revision_ix1 + ON revision (name,revision) + "); + $self->{dbh}->do(" + CREATE INDEX revision_ix2 + ON revision (name,commithash) + "); } - # Construct the revision table if required + # Construct the head table if required unless ( $self->{tables}{head} ) { $self->{dbh}->do(" @@ -2134,6 +2142,10 @@ sub new mode TEXT NOT NULL ) "); + $self->{dbh}->do(" + CREATE INDEX head_ix1 + ON head (name) + "); } # Construct the properties table if required From 1d6a003a42b3c23ad7883b0bbe6a034728e51836 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 23 Oct 2006 00:46:37 -0700 Subject: [PATCH 107/114] git-send-email: do not pass custom Date: header We already generate a Date: header based on when the patch was emailed. git-format-patch includes the Date: header of the patch. Having two Date: headers is just confusing, so we just use the current Date: Often the mailed patches in a patch series are created over a series of several hours or days, so the Date: header from the original commit is incorrect for email, and often far off enough for spam filters to complain. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- git-send-email.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 1c6d2cc787..c42dc3bc94 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -514,7 +514,7 @@ foreach my $t (@files) { $2, $_) unless $quiet; push @cc, $2; } - elsif (/^[-A-Za-z]+:\s+\S/) { + elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } From b4aee09e610567529dc619d7324dc2fe85a11db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Sun, 22 Oct 2006 17:02:42 -0700 Subject: [PATCH 108/114] ignore-errors requires cl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc-git complains that it can't find the definition of ignore-errors unless I (require 'cl). So I guess the correct place to do that is in the file itself. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 80e767533a..8b6361922f 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -33,6 +33,8 @@ ;; - working with revisions other than HEAD ;; +(eval-when-compile (require 'cl)) + (defvar git-commits-coding-system 'utf-8 "Default coding system for git commits.") From 2bb10fe51eae9bf205513b6a1f9197d4599a8c03 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 23 Oct 2006 18:26:39 +0200 Subject: [PATCH 109/114] prune-packed: Fix uninitialized variable. The dryrun variable was made local instead of static by the previous commit, and local variables aren't initialized to zero. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- builtin-prune-packed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index e12b6cf599..24e3b0a8c2 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -56,7 +56,7 @@ void prune_packed_objects(int dryrun) int cmd_prune_packed(int argc, const char **argv, const char *prefix) { int i; - int dryrun; + int dryrun = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; From 0cc6d3464a1944e930cfb12ba030eb1581323845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Santi=20B=C3=A9jar?= Date: Mon, 23 Oct 2006 18:42:14 +0200 Subject: [PATCH 110/114] Documentation for the [remote] config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Santi Béjar Signed-off-by: Junio C Hamano --- Documentation/config.txt | 12 ++++++++++++ Documentation/urls.txt | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 84e38911ee..ee51fe3b9f 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -230,6 +230,18 @@ pull.octopus:: pull.twohead:: The default merge strategy to use when pulling a single branch. +remote..url:: + The URL of a remote repository. See gitlink:git-fetch[1] or + gitlink:git-push[1]. + +remote..fetch:: + The default set of "refspec" for gitlink:git-fetch[1]. See + gitlink:git-fetch[1]. + +remote..push:: + The default set of "refspec" for gitlink:git-push[1]. See + gitlink:git-push[1]. + show.difftree:: The default gitlink:git-diff-tree[1] arguments to be used for gitlink:git-show[1]. diff --git a/Documentation/urls.txt b/Documentation/urls.txt index 26ecba53fb..670827c323 100644 --- a/Documentation/urls.txt +++ b/Documentation/urls.txt @@ -51,6 +51,14 @@ lines are used for `git-push` and `git-fetch`/`git-pull`, respectively. Multiple `Push:` and `Pull:` lines may be specified for additional branch mappings. +Or, equivalently, in the `$GIT_DIR/config` (note the use +of `fetch` instead of `Pull:`): + +[remote ""] + url = + push = + fetch = + The name of a file in `$GIT_DIR/branches` directory can be specified as an older notation short-hand; the named file should contain a single line, a URL in one of the From 810799ecab6f9164401416988d9d79270315ba18 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Mon, 23 Oct 2006 15:59:48 +0200 Subject: [PATCH 111/114] git-clone: honor --quiet I noticed that a cron-launched "git-clone --quiet" was generating progress output to standard error -- and thus always spamming me. The offending output was due to git-clone invoking git-read-tree with its undocumented -v option. This change turns off "-v" for --quiet. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- git-clone.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-clone.sh b/git-clone.sh index bf54a11508..24b119537b 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -414,7 +414,8 @@ Pull: refs/heads/$head_points_at:$origin_track" && case "$no_checkout" in '') - git-read-tree -m -u -v HEAD HEAD + test "z$quiet" = z && v=-v || v= + git-read-tree -m -u $v HEAD HEAD esac fi rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD" From 67aef034551aed0cc417e8b758a59b9978c4f3f1 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Mon, 23 Oct 2006 22:22:25 +0200 Subject: [PATCH 112/114] xdiff/xemit.c (xdl_find_func): Elide trailing white space in a context header. This removes trailing blanks from git-generated diff headers the same way a similar patch did that for GNU diff: http://article.gmane.org/gmane.comp.gnu.utils.bugs/13839 That is, it removes trailing blanks on the hunk header line that shows the function name. Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- xdiff/xemit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 714c563547..154c26fdcd 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -90,7 +90,7 @@ static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll) { *rec == '#')) { /* #define? */ if (len > sz) len = sz; - if (len && rec[len - 1] == '\n') + while (0 < len && isspace((unsigned char)rec[len - 1])) len--; memcpy(buf, rec, len); *ll = len; From 83543a24c316de60b886cd98272fde2bcc99d558 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 23 Oct 2006 18:26:05 -0700 Subject: [PATCH 113/114] daemon: do not die on older clients. In the older times, the clients did not say which host they were trying to connect, and the code we recently added did not quite handle the older clients correctly. Noticed by Simon Arlott. Signed-off-by: Junio C Hamano --- daemon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon.c b/daemon.c index ad8492873e..e66bb802da 100644 --- a/daemon.c +++ b/daemon.c @@ -450,6 +450,8 @@ void fill_in_extra_table_entries(struct interp *itable) * Replace literal host with lowercase-ized hostname. */ hp = interp_table[INTERP_SLOT_HOST].value; + if (!hp) + return; for ( ; *hp; hp++) *hp = tolower(*hp); @@ -544,8 +546,10 @@ static int execute(struct sockaddr *addr) loginfo("Extended attributes (%d bytes) exist <%.*s>", (int) pktlen - len, (int) pktlen - len, line + len + 1); - if (len && line[len-1] == '\n') + if (len && line[len-1] == '\n') { line[--len] = 0; + pktlen--; + } /* * Initialize the path interpolation table for this connection. From a153adf683d2b6e22c7e892ed8a161b140156186 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 24 Oct 2006 02:39:14 +0200 Subject: [PATCH 114/114] gitweb: Fix setting $/ in parse_commit() If the commit couldn't have been read, $/ wasn't restored to \n properly, causing random havoc like git_get_ref_list() returning the ref names with trailing \n. Aside of potential confusion in the body of git_search(), no other $/ surprises are hopefully hidden in the code. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 23b26a2db2..2390603e97 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1009,12 +1009,11 @@ sub parse_commit { if (defined $commit_text) { @commit_lines = @$commit_text; } else { - $/ = "\0"; + local $/ = "\0"; open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id or return; @commit_lines = split '\n', <$fd>; close $fd or return; - $/ = "\n"; pop @commit_lines; } my $header = shift @commit_lines;
' . mode_str('040000') . "'; + print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base, + file_name=>$up)}, + ".."); + print "